From 07a55f2c401c7bab42db8b1fb83abe3f980d364f Mon Sep 17 00:00:00 2001 From: Yohann Roussel <yroussel@google.com> Date: Mon, 7 Apr 2014 11:55:23 +0200 Subject: Initial Jack import part 2. Change-Id: Ic439604a1f030700d9049800fbf62422e0004d35 --- dx/.checkstyle | 10 + dx/.project | 6 + dx/.settings/org.eclipse.jdt.core.prefs | 326 ++ dx/.settings/org.eclipse.jdt.ui.prefs | 7 + dx/jackstyle.xml | 295 ++ dx/src/com/android/jack/dx/Version.java | 4 +- dx/src/com/android/jack/dx/dex/DexFormat.java | 138 +- dx/src/com/android/jack/dx/dex/DexOptions.java | 20 +- dx/src/com/android/jack/dx/dex/SizeOf.java | 161 +- .../com/android/jack/dx/dex/TableOfContents.java | 400 +- dx/src/com/android/jack/dx/dex/code/ArrayData.java | 301 +- .../android/jack/dx/dex/code/BlockAddresses.java | 222 +- .../com/android/jack/dx/dex/code/CatchBuilder.java | 38 +- .../android/jack/dx/dex/code/CatchHandlerList.java | 368 +- .../com/android/jack/dx/dex/code/CatchTable.java | 294 +- .../com/android/jack/dx/dex/code/CodeAddress.java | 110 +- dx/src/com/android/jack/dx/dex/code/CstInsn.java | 302 +- dx/src/com/android/jack/dx/dex/code/DalvCode.java | 389 +- dx/src/com/android/jack/dx/dex/code/DalvInsn.java | 768 ++-- .../com/android/jack/dx/dex/code/DalvInsnList.java | 424 ++- dx/src/com/android/jack/dx/dex/code/Dop.java | 285 +- dx/src/com/android/jack/dx/dex/code/Dops.java | 1771 ++++----- .../android/jack/dx/dex/code/FixedSizeInsn.java | 79 +- .../jack/dx/dex/code/HighRegisterPrefix.java | 195 +- .../com/android/jack/dx/dex/code/InsnFormat.java | 1330 +++---- dx/src/com/android/jack/dx/dex/code/LocalEnd.java | 104 +- dx/src/com/android/jack/dx/dex/code/LocalList.java | 1537 ++++---- .../android/jack/dx/dex/code/LocalSnapshot.java | 108 +- .../com/android/jack/dx/dex/code/LocalStart.java | 118 +- dx/src/com/android/jack/dx/dex/code/OddSpacer.java | 76 +- .../android/jack/dx/dex/code/OutputCollector.java | 156 +- .../android/jack/dx/dex/code/OutputFinisher.java | 1343 ++++--- .../com/android/jack/dx/dex/code/PositionList.java | 288 +- dx/src/com/android/jack/dx/dex/code/RopToDop.java | 1095 +++--- .../android/jack/dx/dex/code/RopTranslator.java | 1444 ++++--- .../com/android/jack/dx/dex/code/SimpleInsn.java | 57 +- .../android/jack/dx/dex/code/StdCatchBuilder.java | 492 ++- .../com/android/jack/dx/dex/code/SwitchData.java | 409 +- .../com/android/jack/dx/dex/code/TargetInsn.java | 204 +- .../android/jack/dx/dex/code/VariableSizeInsn.java | 41 +- .../com/android/jack/dx/dex/code/ZeroSizeInsn.java | 58 +- .../com/android/jack/dx/dex/code/form/Form10t.java | 93 +- .../com/android/jack/dx/dex/code/form/Form10x.java | 73 +- .../com/android/jack/dx/dex/code/form/Form11n.java | 129 +- .../com/android/jack/dx/dex/code/form/Form11x.java | 95 +- .../com/android/jack/dx/dex/code/form/Form12x.java | 226 +- .../com/android/jack/dx/dex/code/form/Form20t.java | 93 +- .../com/android/jack/dx/dex/code/form/Form21c.java | 192 +- .../com/android/jack/dx/dex/code/form/Form21h.java | 163 +- .../com/android/jack/dx/dex/code/form/Form21s.java | 128 +- .../com/android/jack/dx/dex/code/form/Form21t.java | 141 +- .../com/android/jack/dx/dex/code/form/Form22b.java | 134 +- .../com/android/jack/dx/dex/code/form/Form22c.java | 142 +- .../com/android/jack/dx/dex/code/form/Form22s.java | 136 +- .../com/android/jack/dx/dex/code/form/Form22t.java | 148 +- .../com/android/jack/dx/dex/code/form/Form22x.java | 100 +- .../com/android/jack/dx/dex/code/form/Form23x.java | 107 +- .../com/android/jack/dx/dex/code/form/Form30t.java | 87 +- .../com/android/jack/dx/dex/code/form/Form31c.java | 184 +- .../com/android/jack/dx/dex/code/form/Form31i.java | 124 +- .../com/android/jack/dx/dex/code/form/Form31t.java | 131 +- .../com/android/jack/dx/dex/code/form/Form32x.java | 101 +- .../com/android/jack/dx/dex/code/form/Form35c.java | 310 +- .../com/android/jack/dx/dex/code/form/Form3rc.java | 115 +- .../com/android/jack/dx/dex/code/form/Form51l.java | 120 +- .../jack/dx/dex/code/form/SpecialFormat.java | 68 +- .../android/jack/dx/dex/file/AnnotationItem.java | 324 +- .../jack/dx/dex/file/AnnotationSetItem.java | 240 +- .../jack/dx/dex/file/AnnotationSetRefItem.java | 83 +- .../android/jack/dx/dex/file/AnnotationUtils.java | 407 +- .../jack/dx/dex/file/AnnotationsDirectoryItem.java | 609 ++- .../com/android/jack/dx/dex/file/CatchStructs.java | 518 +-- .../android/jack/dx/dex/file/ClassDataItem.java | 662 ++-- .../com/android/jack/dx/dex/file/ClassDefItem.java | 671 ++-- .../android/jack/dx/dex/file/ClassDefsSection.java | 256 +- dx/src/com/android/jack/dx/dex/file/CodeItem.java | 510 ++- .../jack/dx/dex/file/DebugInfoConstants.java | 260 +- .../android/jack/dx/dex/file/DebugInfoDecoder.java | 997 ++--- .../android/jack/dx/dex/file/DebugInfoEncoder.java | 1514 ++++---- .../android/jack/dx/dex/file/DebugInfoItem.java | 297 +- dx/src/com/android/jack/dx/dex/file/DexFile.java | 1184 +++--- .../android/jack/dx/dex/file/EncodedArrayItem.java | 181 +- .../com/android/jack/dx/dex/file/EncodedField.java | 223 +- .../android/jack/dx/dex/file/EncodedMember.java | 99 +- .../android/jack/dx/dex/file/EncodedMethod.java | 277 +- .../jack/dx/dex/file/FieldAnnotationStruct.java | 173 +- .../com/android/jack/dx/dex/file/FieldIdItem.java | 78 +- .../android/jack/dx/dex/file/FieldIdsSection.java | 172 +- .../com/android/jack/dx/dex/file/HeaderItem.java | 156 +- .../android/jack/dx/dex/file/HeaderSection.java | 68 +- dx/src/com/android/jack/dx/dex/file/IdItem.java | 64 +- .../android/jack/dx/dex/file/ImportedCodeItem.java | 30 +- .../jack/dx/dex/file/ImportedDebugInfoItem.java | 8 +- .../com/android/jack/dx/dex/file/IndexedItem.java | 100 +- dx/src/com/android/jack/dx/dex/file/Item.java | 98 +- dx/src/com/android/jack/dx/dex/file/ItemType.java | 127 +- dx/src/com/android/jack/dx/dex/file/MapItem.java | 355 +- .../com/android/jack/dx/dex/file/MemberIdItem.java | 135 +- .../android/jack/dx/dex/file/MemberIdsSection.java | 89 +- .../jack/dx/dex/file/MethodAnnotationStruct.java | 173 +- .../com/android/jack/dx/dex/file/MethodIdItem.java | 78 +- .../android/jack/dx/dex/file/MethodIdsSection.java | 172 +- .../android/jack/dx/dex/file/MixedItemSection.java | 563 ++- .../android/jack/dx/dex/file/OffsettedItem.java | 520 ++- .../dx/dex/file/ParameterAnnotationStruct.java | 231 +- .../com/android/jack/dx/dex/file/ProtoIdItem.java | 226 +- .../android/jack/dx/dex/file/ProtoIdsSection.java | 180 +- dx/src/com/android/jack/dx/dex/file/Section.java | 474 ++- .../com/android/jack/dx/dex/file/Statistics.java | 283 +- .../android/jack/dx/dex/file/StringDataItem.java | 114 +- .../com/android/jack/dx/dex/file/StringIdItem.java | 178 +- .../android/jack/dx/dex/file/StringIdsSection.java | 251 +- .../com/android/jack/dx/dex/file/TypeIdItem.java | 70 +- .../android/jack/dx/dex/file/TypeIdsSection.java | 258 +- .../com/android/jack/dx/dex/file/TypeListItem.java | 171 +- .../jack/dx/dex/file/UniformItemSection.java | 150 +- .../android/jack/dx/dex/file/UniformListItem.java | 320 +- .../com/android/jack/dx/dex/file/ValueEncoder.java | 840 ++--- dx/src/com/android/jack/dx/io/Annotation.java | 136 +- dx/src/com/android/jack/dx/io/ClassData.java | 153 +- dx/src/com/android/jack/dx/io/ClassDef.java | 138 +- dx/src/com/android/jack/dx/io/Code.java | 190 +- dx/src/com/android/jack/dx/io/CodeReader.java | 189 +- dx/src/com/android/jack/dx/io/DexBuffer.java | 1164 +++--- dx/src/com/android/jack/dx/io/DexHasher.java | 82 +- dx/src/com/android/jack/dx/io/DexIndexPrinter.java | 157 +- dx/src/com/android/jack/dx/io/EncodedValue.java | 60 +- .../com/android/jack/dx/io/EncodedValueReader.java | 227 +- dx/src/com/android/jack/dx/io/FieldId.java | 79 +- dx/src/com/android/jack/dx/io/IndexType.java | 40 +- dx/src/com/android/jack/dx/io/MethodId.java | 82 +- dx/src/com/android/jack/dx/io/OpcodeInfo.java | 1867 ++++----- dx/src/com/android/jack/dx/io/Opcodes.java | 608 +-- dx/src/com/android/jack/dx/io/ProtoId.java | 78 +- .../jack/dx/io/instructions/AddressMap.java | 45 +- .../jack/dx/io/instructions/BaseCodeCursor.java | 76 +- .../jack/dx/io/instructions/CodeCursor.java | 44 +- .../android/jack/dx/io/instructions/CodeInput.java | 34 +- .../jack/dx/io/instructions/CodeOutput.java | 88 +- .../dx/io/instructions/DecodedInstruction.java | 733 ++-- .../FillArrayDataPayloadDecodedInstruction.java | 125 +- .../FiveRegisterDecodedInstruction.java | 155 +- .../FourRegisterDecodedInstruction.java | 112 +- .../jack/dx/io/instructions/InstructionCodec.java | 1688 ++++----- .../OneRegisterDecodedInstruction.java | 71 +- .../PackedSwitchPayloadDecodedInstruction.java | 79 +- .../RegisterRangeDecodedInstruction.java | 81 +- .../dx/io/instructions/ShortArrayCodeInput.java | 83 +- .../dx/io/instructions/ShortArrayCodeOutput.java | 226 +- .../SparseSwitchPayloadDecodedInstruction.java | 71 +- .../ThreeRegisterDecodedInstruction.java | 95 +- .../TwoRegisterDecodedInstruction.java | 78 +- .../ZeroRegisterDecodedInstruction.java | 43 +- .../com/android/jack/dx/merge/CollisionPolicy.java | 18 +- dx/src/com/android/jack/dx/merge/DexMerger.java | 1865 ++++----- dx/src/com/android/jack/dx/merge/IndexMap.java | 454 +-- .../jack/dx/merge/InstructionTransformer.java | 142 +- dx/src/com/android/jack/dx/merge/SortableType.java | 135 +- dx/src/com/android/jack/dx/merge/TypeList.java | 59 +- .../android/jack/dx/rop/annotation/Annotation.java | 329 +- .../dx/rop/annotation/AnnotationVisibility.java | 34 +- .../jack/dx/rop/annotation/Annotations.java | 319 +- .../jack/dx/rop/annotation/AnnotationsList.java | 116 +- .../jack/dx/rop/annotation/NameValuePair.java | 141 +- .../com/android/jack/dx/rop/code/AccessFlags.java | 700 ++-- .../com/android/jack/dx/rop/code/BasicBlock.java | 457 ++- .../android/jack/dx/rop/code/BasicBlockList.java | 637 ++-- .../dx/rop/code/ConservativeTranslationAdvice.java | 49 +- dx/src/com/android/jack/dx/rop/code/CstInsn.java | 86 +- .../jack/dx/rop/code/DexTranslationAdvice.java | 189 +- .../com/android/jack/dx/rop/code/Exceptions.java | 206 +- .../jack/dx/rop/code/FillArrayDataInsn.java | 162 +- dx/src/com/android/jack/dx/rop/code/Insn.java | 737 ++-- dx/src/com/android/jack/dx/rop/code/InsnList.java | 193 +- dx/src/com/android/jack/dx/rop/code/LocalItem.java | 258 +- .../jack/dx/rop/code/LocalVariableExtractor.java | 280 +- .../jack/dx/rop/code/LocalVariableInfo.java | 415 +- .../com/android/jack/dx/rop/code/PlainCstInsn.java | 92 +- dx/src/com/android/jack/dx/rop/code/PlainInsn.java | 220 +- dx/src/com/android/jack/dx/rop/code/RegOps.java | 778 ++-- .../com/android/jack/dx/rop/code/RegisterSpec.java | 1180 +++--- .../android/jack/dx/rop/code/RegisterSpecList.java | 666 ++-- .../android/jack/dx/rop/code/RegisterSpecSet.java | 662 ++-- dx/src/com/android/jack/dx/rop/code/Rop.java | 701 ++-- dx/src/com/android/jack/dx/rop/code/RopMethod.java | 320 +- dx/src/com/android/jack/dx/rop/code/Rops.java | 3977 ++++++++++---------- .../android/jack/dx/rop/code/SourcePosition.java | 246 +- .../com/android/jack/dx/rop/code/SwitchInsn.java | 169 +- .../android/jack/dx/rop/code/ThrowingCstInsn.java | 139 +- .../com/android/jack/dx/rop/code/ThrowingInsn.java | 164 +- .../jack/dx/rop/code/TranslationAdvice.java | 67 +- dx/src/com/android/jack/dx/rop/cst/Constant.java | 78 +- .../com/android/jack/dx/rop/cst/ConstantPool.java | 86 +- .../com/android/jack/dx/rop/cst/CstAnnotation.java | 119 +- dx/src/com/android/jack/dx/rop/cst/CstArray.java | 224 +- .../android/jack/dx/rop/cst/CstBaseMethodRef.java | 221 +- dx/src/com/android/jack/dx/rop/cst/CstBoolean.java | 131 +- dx/src/com/android/jack/dx/rop/cst/CstByte.java | 128 +- dx/src/com/android/jack/dx/rop/cst/CstChar.java | 128 +- dx/src/com/android/jack/dx/rop/cst/CstDouble.java | 110 +- dx/src/com/android/jack/dx/rop/cst/CstEnumRef.java | 73 +- .../com/android/jack/dx/rop/cst/CstFieldRef.java | 96 +- dx/src/com/android/jack/dx/rop/cst/CstFloat.java | 112 +- .../com/android/jack/dx/rop/cst/CstIndexMap.java | 31 +- dx/src/com/android/jack/dx/rop/cst/CstInteger.java | 175 +- .../jack/dx/rop/cst/CstInterfaceMethodRef.java | 67 +- .../com/android/jack/dx/rop/cst/CstKnownNull.java | 148 +- .../com/android/jack/dx/rop/cst/CstLiteral32.java | 108 +- .../com/android/jack/dx/rop/cst/CstLiteral64.java | 108 +- .../android/jack/dx/rop/cst/CstLiteralBits.java | 105 +- dx/src/com/android/jack/dx/rop/cst/CstLong.java | 107 +- .../com/android/jack/dx/rop/cst/CstMemberRef.java | 180 +- .../com/android/jack/dx/rop/cst/CstMethodRef.java | 31 +- dx/src/com/android/jack/dx/rop/cst/CstNat.java | 265 +- dx/src/com/android/jack/dx/rop/cst/CstShort.java | 130 +- dx/src/com/android/jack/dx/rop/cst/CstString.java | 666 ++-- dx/src/com/android/jack/dx/rop/cst/CstType.java | 429 +-- .../android/jack/dx/rop/cst/StdConstantPool.java | 193 +- .../com/android/jack/dx/rop/cst/TypedConstant.java | 47 +- dx/src/com/android/jack/dx/rop/cst/Zeroes.java | 64 +- dx/src/com/android/jack/dx/rop/type/Prototype.java | 647 ++-- .../com/android/jack/dx/rop/type/StdTypeList.java | 670 ++-- dx/src/com/android/jack/dx/rop/type/Type.java | 1604 ++++---- .../com/android/jack/dx/rop/type/TypeBearer.java | 91 +- dx/src/com/android/jack/dx/rop/type/TypeList.java | 84 +- .../android/jack/dx/ssa/BasicRegisterMapper.java | 173 +- dx/src/com/android/jack/dx/ssa/ConstCollector.java | 626 ++- .../com/android/jack/dx/ssa/DeadCodeRemover.java | 409 +- dx/src/com/android/jack/dx/ssa/DomFront.java | 282 +- dx/src/com/android/jack/dx/ssa/Dominators.java | 414 +- dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java | 1464 ++++--- .../jack/dx/ssa/InterferenceRegisterMapper.java | 240 +- .../com/android/jack/dx/ssa/LiteralOpUpgrader.java | 297 +- .../jack/dx/ssa/LocalVariableExtractor.java | 294 +- .../com/android/jack/dx/ssa/LocalVariableInfo.java | 403 +- .../com/android/jack/dx/ssa/MoveParamCombiner.java | 237 +- dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java | 429 +-- dx/src/com/android/jack/dx/ssa/Optimizer.java | 379 +- dx/src/com/android/jack/dx/ssa/PhiInsn.java | 658 ++-- .../com/android/jack/dx/ssa/PhiTypeResolver.java | 384 +- dx/src/com/android/jack/dx/ssa/RegisterMapper.java | 61 +- dx/src/com/android/jack/dx/ssa/SCCP.java | 1191 +++--- dx/src/com/android/jack/dx/ssa/SetFactory.java | 115 +- dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java | 1864 +++++---- dx/src/com/android/jack/dx/ssa/SsaConverter.java | 635 ++-- dx/src/com/android/jack/dx/ssa/SsaInsn.java | 496 +-- dx/src/com/android/jack/dx/ssa/SsaMethod.java | 1660 ++++---- dx/src/com/android/jack/dx/ssa/SsaRenamer.java | 1046 +++-- .../jack/dx/ssa/back/FirstFitAllocator.java | 189 +- .../ssa/back/FirstFitLocalCombiningAllocator.java | 1916 +++++----- .../jack/dx/ssa/back/IdenticalBlockCombiner.java | 265 +- .../jack/dx/ssa/back/InterferenceGraph.java | 147 +- .../android/jack/dx/ssa/back/LivenessAnalyzer.java | 449 ++- .../jack/dx/ssa/back/NullRegisterAllocator.java | 50 +- .../jack/dx/ssa/back/RegisterAllocator.java | 278 +- dx/src/com/android/jack/dx/ssa/back/SsaToRop.java | 557 ++- .../com/android/jack/dx/util/AnnotatedOutput.java | 101 +- dx/src/com/android/jack/dx/util/BitIntSet.java | 216 +- dx/src/com/android/jack/dx/util/Bits.java | 394 +- dx/src/com/android/jack/dx/util/ByteArray.java | 589 ++- .../jack/dx/util/ByteArrayAnnotatedOutput.java | 1048 +++--- .../android/jack/dx/util/ByteArrayByteInput.java | 20 +- dx/src/com/android/jack/dx/util/ByteInput.java | 12 +- dx/src/com/android/jack/dx/util/ByteOutput.java | 12 +- dx/src/com/android/jack/dx/util/DexException.java | 15 +- .../android/jack/dx/util/ExceptionWithContext.java | 236 +- dx/src/com/android/jack/dx/util/FileUtils.java | 128 +- dx/src/com/android/jack/dx/util/FixedSizeList.java | 451 ++- dx/src/com/android/jack/dx/util/Hex.java | 507 +-- dx/src/com/android/jack/dx/util/HexParser.java | 226 +- .../com/android/jack/dx/util/IndentingWriter.java | 240 +- dx/src/com/android/jack/dx/util/IntIterator.java | 26 +- dx/src/com/android/jack/dx/util/IntList.java | 735 ++-- dx/src/com/android/jack/dx/util/IntSet.java | 76 +- dx/src/com/android/jack/dx/util/LabeledItem.java | 12 +- dx/src/com/android/jack/dx/util/LabeledList.java | 289 +- dx/src/com/android/jack/dx/util/Leb128Utils.java | 244 +- dx/src/com/android/jack/dx/util/ListIntSet.java | 203 +- .../android/jack/dx/util/MutabilityControl.java | 108 +- .../android/jack/dx/util/MutabilityException.java | 24 +- dx/src/com/android/jack/dx/util/Mutf8.java | 158 +- dx/src/com/android/jack/dx/util/Output.java | 185 +- dx/src/com/android/jack/dx/util/ToHuman.java | 14 +- .../com/android/jack/dx/util/TwoColumnOutput.java | 407 +- dx/src/com/android/jack/dx/util/Uint.java | 15 +- dx/src/com/android/jack/dx/util/Unsigned.java | 30 +- dx/src/com/android/jack/dx/util/Warning.java | 19 +- dx/src/com/android/jack/dx/util/Writers.java | 40 +- 288 files changed, 46888 insertions(+), 46825 deletions(-) create mode 100644 dx/.checkstyle create mode 100644 dx/.settings/org.eclipse.jdt.ui.prefs create mode 100644 dx/jackstyle.xml (limited to 'dx') diff --git a/dx/.checkstyle b/dx/.checkstyle new file mode 100644 index 0000000..16b1f09 --- /dev/null +++ b/dx/.checkstyle @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false"> + <local-check-config name="Jack Checkstyle" location="jackstyle.xml" type="project" description=""> + <additional-data name="protect-config-file" value="true"/> + </local-check-config> + <fileset name="all" enabled="true" check-config-name="Jack Checkstyle" local="true"> + <file-match-pattern match-pattern="." include-pattern="true"/> + </fileset> +</fileset-config> diff --git a/dx/.project b/dx/.project index b9adec6..08f639b 100644 --- a/dx/.project +++ b/dx/.project @@ -10,8 +10,14 @@ <arguments> </arguments> </buildCommand> + <buildCommand> + <name>net.sf.eclipsecs.core.CheckstyleBuilder</name> + <arguments> + </arguments> + </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> + <nature>net.sf.eclipsecs.core.CheckstyleNature</nature> </natures> </projectDescription> diff --git a/dx/.settings/org.eclipse.jdt.core.prefs b/dx/.settings/org.eclipse.jdt.core.prefs index 8000cd6..942db7e 100644 --- a/dx/.settings/org.eclipse.jdt.core.prefs +++ b/dx/.settings/org.eclipse.jdt.core.prefs @@ -9,3 +9,329 @@ org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field.count_dependent=1585|-1|1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable.count_dependent=1585|-1|1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method.count_dependent=1585|-1|1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package.count_dependent=1585|-1|1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=1040 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter.count_dependent=1040|-1|1040 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=1585 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type.count_dependent=1585|-1|1585 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression.count_dependent=16|5|80 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation.count_dependent=16|-1|16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant.count_dependent=16|-1|16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call.count_dependent=16|5|80 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation.count_dependent=16|5|80 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression.count_dependent=16|4|80 +org.eclipse.jdt.core.formatter.alignment_for_assignment=16 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression.count_dependent=16|-1|16 +org.eclipse.jdt.core.formatter.alignment_for_cascading_method_invocation_with_arguments=16 +org.eclipse.jdt.core.formatter.alignment_for_cascading_method_invocation_with_arguments.count_dependent=16|-1|16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants.count_dependent=16|5|48 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer.count_dependent=16|5|80 +org.eclipse.jdt.core.formatter.alignment_for_field_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_for_statement=16 +org.eclipse.jdt.core.formatter.alignment_for_generic_type_arguments=16 +org.eclipse.jdt.core.formatter.alignment_for_generic_type_arguments.count_dependent=16|-1|16 +org.eclipse.jdt.core.formatter.alignment_for_local_variable_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields.count_dependent=16|-1|16 +org.eclipse.jdt.core.formatter.alignment_for_new_anonymous_class=20 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration.count_dependent=16|5|80 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration.count_dependent=16|5|80 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation.count_dependent=16|4|48 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration.count_dependent=16|4|49 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration.count_dependent=16|4|48 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration.count_dependent=16|4|48 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration.count_dependent=16|4|48 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=0 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=100 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.comment_new_line_at_start_of_html_paragraph=true +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.force_if_else_statement_brace=true +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comment_prefix=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=100 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=2 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_comment_inline_tags=false +org.eclipse.jdt.core.formatter.wrap_non_simple_local_variable_annotation=true +org.eclipse.jdt.core.formatter.wrap_non_simple_member_annotation=true +org.eclipse.jdt.core.formatter.wrap_non_simple_package_annotation=true +org.eclipse.jdt.core.formatter.wrap_non_simple_parameter_annotation=false +org.eclipse.jdt.core.formatter.wrap_non_simple_type_annotation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.formatter.wrap_prefer_two_fragments=false diff --git a/dx/.settings/org.eclipse.jdt.ui.prefs b/dx/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..238808d --- /dev/null +++ b/dx/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +formatter_profile=_Jack Format 100 +formatter_settings_version=13 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=com.google;android;antenna;antlr;ar;asposewobfuscated;asquare;atg;au;beaver;bibtex;bmsi;bsh;ccl;cern;ChartDirector;checkers;com;COM;common;contribs;corejava;cryptix;cybervillains;dalvik;danbikel;de;EDU;eg;eu;examples;fat;fit;fitlibrary;fmpp;freemarker;gnu;groovy;groovyjarjarantlr;groovyjarjarasm;hak;hep;ie;imageinfo;info;it;jal;Jama;japa;japacheckers;jas;jasmin;javancss;javanet;javassist;javazoom;java_cup;jcifs;jetty;JFlex;jj2000;jline;jp;JSci;jsr166y;junit;jxl;jxxload_help;kawa;kea;libcore;libsvm;lti;memetic;mt;mx4j;net;netscape;nl;nu;oauth;ognl;opennlp;oracle;org;penn2dg;pennconverter;pl;prefuse;proguard;repackage;scm;se;serp;simple;soot;sqlj;src;ssa;sun;sunlabs;tcl;testdata;testshell;testsuite;twitter4j;uk;ViolinStrings;weka;wet;winstone;woolfel;wowza;;java;javax; +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.staticondemandthreshold=99 diff --git a/dx/jackstyle.xml b/dx/jackstyle.xml new file mode 100644 index 0000000..59286c8 --- /dev/null +++ b/dx/jackstyle.xml @@ -0,0 +1,295 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE module PUBLIC + "-//Puppy Crawl//DTD Check Configuration 1.3//EN" + "http://www.puppycrawl.com/dtds/configuration_1_3.dtd"> + +<!-- This is a checkstyle configuration file. For descriptions of +what the following rules do, please see the checkstyle configuration +page at http://checkstyle.sourceforge.net/config.html --> + +<!-- Checks with numbered comments refer to recommendations made +by Joshua Bloch in his book Effective Java --> + +<module name="Checker"> + <property name="charset" value="UTF-8"/> + <module name="FileTabCharacter"> + <!-- Checks that there are no tab characters in the file. + --> + </module> + + <module name="RegexpSingleline"> + <!-- Checks that FIXME is not used in comments. TODO is preferred. + --> + <property name="format" value="((//.*)|(\*.*))FIXME" /> + <property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' /> + </module> + + <module name="RegexpSingleline"> + <!-- Checks that TODOs are properly formatted. + + The (?<!TODO\(.{0,100}) makes the regex ignore any secondary TODO's on the line + so that things like //TODO(bob): remove this TODO on 1/1/2020 don't trigger a warning + because of the second TODO. (The {0,100} is because java doesn't recoginize arbitrary + length look backs, but we know each java line should be < 100 chars.) + --> + <property name="format" value="((//.*)|(\*.*))(?<!TODO\(.{0,100})(TODO[^(])|(TODO\([^)]*$)" /> + <property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' /> + </module> + + + <!-- All Java AST specific tests live under TreeWalker module. --> + <module name="TreeWalker"> + + <!-- + + IMPORT CHECKS + + --> + + <module name="RedundantImport"> + <property name="severity" value="error"/> + </module> + + <module name="AvoidStarImport"> + <property name="severity" value="error"/> + </module> + + <module name="UnusedImports"> + <!-- DPL is a notable violator of this rule. --> + <property name="severity" value="error"/> + <!-- Imports used only in Javadoc are tolerated. --> + <property name="processJavadoc" value="true"/> + <message + key="import.unused" + value="Unused import: {0}." /> + </module> + + <module name="ImportOrder"> + <!-- Checks for out of order import statements. --> + <property name="severity" value="warning"/> + <property name="groups" value="com.google,*,java,javax"/> + <!-- This ensures that static imports go first. --> + <property name="option" value="top"/> + <property name="tokens" value="STATIC_IMPORT, IMPORT"/> + </module> + + <!-- + + NAMING CHECKS + + --> + + <!-- Item 38 - Adhere to generally accepted naming conventions --> + + <module name="PackageName"> + <!-- Validates identifiers for package names against the + supplied expression. --> + <!-- Here the default checkstyle rule restricts package name parts to + seven characters, this is not in line with common practice at Google. + --> + <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/> + <property name="severity" value="warning"/> + </module> + + <module name="TypeNameCheck"> + <metadata name="altname" value="TypeName"/> + <property name="severity" value="warning"/> + </module> + + <module name="StaticVariableNameCheck"> + <!-- Validates static, non-final fields against the supplied + expression "^[a-z][a-zA-Z0-9]*?$". --> + <metadata name="altname" value="StaticVariableName"/> + <property name="applyToPublic" value="true"/> + <property name="applyToProtected" value="true"/> + <property name="applyToPackage" value="true"/> + <property name="applyToPrivate" value="true"/> + <property name="format" value="^[a-z][a-zA-Z0-9]*?$"/> + <property name="severity" value="warning"/> + </module> + + <module name="MemberNameCheck"> + <!-- Validates non-static members against the supplied expression. --> + <metadata name="altname" value="MemberName"/> + <property name="applyToPublic" value="true"/> + <property name="applyToProtected" value="true"/> + <property name="applyToPackage" value="true"/> + <property name="applyToPrivate" value="true"/> + <property name="format" value="^[a-z][a-zA-Z0-9]*?$"/> + <property name="severity" value="warning"/> + </module> + + <module name="MethodNameCheck"> + <!-- Validates identifiers for method names. --> + <metadata name="altname" value="MethodName"/> + <property name="format" value="^[a-z][a-zA-Z0-9]*([a-zA-Z0-9]+)*$"/> + <property name="severity" value="warning"/> + </module> + + <module name="ParameterName"> + <!-- Validates identifiers for method parameters against the + expression "^[a-z][a-zA-Z0-9]*$". --> + <property name="severity" value="warning"/> + </module> + + <module name="LocalFinalVariableName"> + <!-- Validates identifiers for local final variables against the + expression "^[a-z][a-zA-Z0-9]*$". --> + <property name="severity" value="warning"/> + </module> + + <module name="LocalVariableName"> + <!-- Validates identifiers for local variables against the + expression "^[a-z][a-zA-Z0-9]*$". --> + <property name="severity" value="warning"/> + </module> + + + <!-- + + LENGTH and CODING CHECKS + + --> + + <module name="LineLength"> + <!-- Checks if a line is too long. --> + <property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="100"/> + <property name="severity" value="error"/> + + <!-- + The default ignore pattern exempts the following elements: + - import statements + - long URLs inside comments + --> + + <property name="ignorePattern" + value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}" + default="^(package .*;\s*)|(import .*;\s*)|( *\* *https?://.*)|(\s*@[\w\.\$]+::\w+(?:\([^\(]*\)|\(\)\(\))?[,;]?)|(\s+\* \{@(link|see) [^\s][^\}]*\}[\.,;]?)$"/> + </module> + + <module name="LeftCurly"> + <!-- Checks for placement of the left curly brace ('{'). --> + <property name="severity" value="warning"/> + </module> + + <module name="RightCurly"> + <!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on + the same line. e.g., the following example is fine: + <pre> + if { + ... + } else + </pre> + --> + <!-- This next example is not fine: + <pre> + if { + ... + } + else + </pre> + --> + <property name="option" value="same"/> + <property name="severity" value="warning"/> + </module> + + <!-- Checks for braces around if and else blocks --> + <module name="NeedBraces"> + <property name="severity" value="warning"/> + <property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/> + </module> + + <module name="UpperEll"> + <!-- Checks that long constants are defined with an upper ell.--> + <property name="severity" value="error"/> + </module> + + <module name="FallThrough"> + <!-- Warn about falling through to the next case statement. Similar to + javac -Xlint:fallthrough, but the check is suppressed if there is a single-line comment + on the last non-blank line preceding the fallen-into case. + --> + <property name="reliefPattern" + value=".*"/> + <property name="severity" value="error"/> + </module> + + + <!-- + + MODIFIERS CHECKS + + --> + + <module name="ModifierOrder"> + <!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and + 8.4.3. The prescribed order is: + public, protected, private, abstract, static, final, transient, volatile, + synchronized, native, strictfp + --> + </module> + + + <!-- + + WHITESPACE CHECKS + + --> + + <module name="WhitespaceAround"> + <!-- Checks that various tokens are surrounded by whitespace. + This includes most binary operators and keywords followed + by regular or curly braces. + --> + <property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR, + BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, + EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, + LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, + LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, + MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, + SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/> + <property name="severity" value="error"/> + </module> + + <module name="WhitespaceAfter"> + <!-- Checks that commas, semicolons and typecasts are followed by + whitespace. + --> + <property name="tokens" value="COMMA, SEMI, TYPECAST"/> + </module> + + <module name="NoWhitespaceAfter"> + <!-- Checks that there is no whitespace after various unary operators. + Linebreaks are allowed. + --> + <property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, + UNARY_PLUS"/> + <property name="allowLineBreaks" value="true"/> + <property name="severity" value="error"/> + </module> + + <module name="NoWhitespaceBefore"> + <!-- Checks that there is no whitespace before various unary operators. + Linebreaks are allowed. + --> + <property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/> + <property name="allowLineBreaks" value="true"/> + <property name="severity" value="error"/> + </module> + + <module name="ParenPad"> + <!-- Checks that there is no whitespace before close parens or after + open parens. + --> + <property name="severity" value="warning"/> + </module> + + <!-- + + MISC CHECKS + + --> + + </module> +</module> + diff --git a/dx/src/com/android/jack/dx/Version.java b/dx/src/com/android/jack/dx/Version.java index 1152dd8..2158cae 100644 --- a/dx/src/com/android/jack/dx/Version.java +++ b/dx/src/com/android/jack/dx/Version.java @@ -20,6 +20,6 @@ package com.android.jack.dx; * Version number for dx. */ public class Version { - /** {@code non-null;} version string */ - public static final String VERSION = "1.7"; + /** {@code non-null;} version string */ + public static final String VERSION = "1.7"; } diff --git a/dx/src/com/android/jack/dx/dex/DexFormat.java b/dx/src/com/android/jack/dx/dex/DexFormat.java index bf526bc..19da4f6 100644 --- a/dx/src/com/android/jack/dx/dex/DexFormat.java +++ b/dx/src/com/android/jack/dx/dex/DexFormat.java @@ -21,78 +21,78 @@ package com.android.jack.dx.dex; * files, and helper methods for same. */ public final class DexFormat { - private DexFormat() {} - - /** - * API level to target in order to produce the most modern file - * format - */ - public static final int API_CURRENT = 14; - - /** API level to target in order to suppress extended opcode usage */ - public static final int API_NO_EXTENDED_OPCODES = 13; - - /** - * file name of the primary {@code .dex} file inside an - * application or library {@code .jar} file - */ - public static final String DEX_IN_JAR_NAME = "classes.dex"; - - /** common prefix for all dex file "magic numbers" */ - public static final String MAGIC_PREFIX = "dex\n"; - - /** common suffix for all dex file "magic numbers" */ - public static final String MAGIC_SUFFIX = "\0"; - - /** dex file version number for the current format variant */ - public static final String VERSION_CURRENT = "036"; - - /** dex file version number for API level 13 and earlier */ - public static final String VERSION_FOR_API_13 = "035"; - - /** - * value used to indicate endianness of file contents - */ - public static final int ENDIAN_TAG = 0x12345678; - - /** - * Returns the API level corresponding to the given magic number, - * or {@code -1} if the given array is not a well-formed dex file - * magic number. - */ - public static int magicToApi(byte[] magic) { - if (magic.length != 8) { - return -1; - } - - if ((magic[0] != 'd') || (magic[1] != 'e') || (magic[2] != 'x') || (magic[3] != '\n') || - (magic[7] != '\0')) { - return -1; - } - - String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]); - - if (version.equals(VERSION_CURRENT)) { - return API_CURRENT; - } else if (version.equals(VERSION_FOR_API_13)) { - return 13; - } - - return -1; + private DexFormat() {} + + /** + * API level to target in order to produce the most modern file + * format + */ + public static final int API_CURRENT = 14; + + /** API level to target in order to suppress extended opcode usage */ + public static final int API_NO_EXTENDED_OPCODES = 13; + + /** + * file name of the primary {@code .dex} file inside an + * application or library {@code .jar} file + */ + public static final String DEX_IN_JAR_NAME = "classes.dex"; + + /** common prefix for all dex file "magic numbers" */ + public static final String MAGIC_PREFIX = "dex\n"; + + /** common suffix for all dex file "magic numbers" */ + public static final String MAGIC_SUFFIX = "\0"; + + /** dex file version number for the current format variant */ + public static final String VERSION_CURRENT = "036"; + + /** dex file version number for API level 13 and earlier */ + public static final String VERSION_FOR_API_13 = "035"; + + /** + * value used to indicate endianness of file contents + */ + public static final int ENDIAN_TAG = 0x12345678; + + /** + * Returns the API level corresponding to the given magic number, + * or {@code -1} if the given array is not a well-formed dex file + * magic number. + */ + public static int magicToApi(byte[] magic) { + if (magic.length != 8) { + return -1; } - /** - * Returns the magic number corresponding to the given target API level. - */ - public static String apiToMagic(int targetApiLevel) { - String version; + if ((magic[0] != 'd') || (magic[1] != 'e') || (magic[2] != 'x') || (magic[3] != '\n') + || (magic[7] != '\0')) { + return -1; + } + + String version = "" + ((char) magic[4]) + ((char) magic[5]) + ((char) magic[6]); + + if (version.equals(VERSION_CURRENT)) { + return API_CURRENT; + } else if (version.equals(VERSION_FOR_API_13)) { + return 13; + } + + return -1; + } - if (targetApiLevel >= API_CURRENT) { - version = VERSION_CURRENT; - } else { - version = VERSION_FOR_API_13; - } + /** + * Returns the magic number corresponding to the given target API level. + */ + public static String apiToMagic(int targetApiLevel) { + String version; - return MAGIC_PREFIX + version + MAGIC_SUFFIX; + if (targetApiLevel >= API_CURRENT) { + version = VERSION_CURRENT; + } else { + version = VERSION_FOR_API_13; } + + return MAGIC_PREFIX + version + MAGIC_SUFFIX; + } } diff --git a/dx/src/com/android/jack/dx/dex/DexOptions.java b/dx/src/com/android/jack/dx/dex/DexOptions.java index 81cf288..2fda5c1 100644 --- a/dx/src/com/android/jack/dx/dex/DexOptions.java +++ b/dx/src/com/android/jack/dx/dex/DexOptions.java @@ -20,16 +20,16 @@ package com.android.jack.dx.dex; * Container for options used to control details of dex file generation. */ public class DexOptions { - /** target API level */ - public int targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; + /** target API level */ + public int targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; - /** force generation of jumbo opcodes */ - public boolean forceJumbo = false; + /** force generation of jumbo opcodes */ + public boolean forceJumbo = false; - /** - * Gets the dex file magic number corresponding to this instance. - */ - public String getMagic() { - return DexFormat.apiToMagic(targetApiLevel); - } + /** + * Gets the dex file magic number corresponding to this instance. + */ + public String getMagic() { + return DexFormat.apiToMagic(targetApiLevel); + } } diff --git a/dx/src/com/android/jack/dx/dex/SizeOf.java b/dx/src/com/android/jack/dx/dex/SizeOf.java index ae118d0..d9441ab 100644 --- a/dx/src/com/android/jack/dx/dex/SizeOf.java +++ b/dx/src/com/android/jack/dx/dex/SizeOf.java @@ -16,95 +16,98 @@ package com.android.jack.dx.dex; +/** + * TODO(jack team) + */ public final class SizeOf { - private SizeOf() {} + private SizeOf() {} - public static final int UBYTE = 1; - public static final int USHORT = 2; - public static final int UINT = 4; + public static final int UBYTE = 1; + public static final int USHORT = 2; + public static final int UINT = 4; - public static final int SIGNATURE = UBYTE * 20; + public static final int SIGNATURE = UBYTE * 20; - /** - * magic ubyte[8] - * checksum uint - * signature ubyte[20] - * file_size uint - * header_size uint - * endian_tag uint - * link_size uint - * link_off uint - * map_off uint - * string_ids_size uint - * string_ids_off uint - * type_ids_size uint - * type_ids_off uint - * proto_ids_size uint - * proto_ids_off uint - * field_ids_size uint - * field_ids_off uint - * method_ids_size uint - * method_ids_off uint - * class_defs_size uint - * class_defs_off uint - * data_size uint - * data_off uint - */ - public static final int HEADER_ITEM = (8 * UBYTE) + UINT + SIGNATURE + (20 * UINT); // 0x70 + /** + * magic ubyte[8] + * checksum uint + * signature ubyte[20] + * file_size uint + * header_size uint + * endian_tag uint + * link_size uint + * link_off uint + * map_off uint + * string_ids_size uint + * string_ids_off uint + * type_ids_size uint + * type_ids_off uint + * proto_ids_size uint + * proto_ids_off uint + * field_ids_size uint + * field_ids_off uint + * method_ids_size uint + * method_ids_off uint + * class_defs_size uint + * class_defs_off uint + * data_size uint + * data_off uint + */ + public static final int HEADER_ITEM = (8 * UBYTE) + UINT + SIGNATURE + (20 * UINT); // 0x70 - /** - * string_data_off uint - */ - public static final int STRING_ID_ITEM = UINT; + /** + * string_data_off uint + */ + public static final int STRING_ID_ITEM = UINT; - /** - * descriptor_idx uint - */ - public static final int TYPE_ID_ITEM = UINT; + /** + * descriptor_idx uint + */ + public static final int TYPE_ID_ITEM = UINT; - /** - * type_idx ushort - */ - public static final int TYPE_ITEM = USHORT; + /** + * type_idx ushort + */ + public static final int TYPE_ITEM = USHORT; - /** - * shorty_idx uint - * return_type_idx uint - * return_type_idx uint - */ - public static final int PROTO_ID_ITEM = UINT + UINT + UINT; + /** + * shorty_idx uint + * return_type_idx uint + * return_type_idx uint + */ + public static final int PROTO_ID_ITEM = UINT + UINT + UINT; - /** - * class_idx ushort - * type_idx/proto_idx ushort - * name_idx uint - */ - public static final int MEMBER_ID_ITEM = USHORT + USHORT + UINT; + /** + * class_idx ushort + * type_idx/proto_idx ushort + * name_idx uint + */ + public static final int MEMBER_ID_ITEM = USHORT + USHORT + UINT; - /** - * class_idx uint - * access_flags uint - * superclass_idx uint - * interfaces_off uint - * source_file_idx uint - * annotations_off uint - * class_data_off uint - * static_values_off uint - */ - public static final int CLASS_DEF_ITEM = 8 * UINT; + /** + * class_idx uint + * access_flags uint + * superclass_idx uint + * interfaces_off uint + * source_file_idx uint + * annotations_off uint + * class_data_off uint + * static_values_off uint + */ + public static final int CLASS_DEF_ITEM = 8 * UINT; - /** - * type ushort - * unused ushort - * size uint - * offset uint - */ - public static final int MAP_ITEM = USHORT + USHORT + UINT + UINT; + /** + * type ushort + * unused ushort + * size uint + * offset uint + */ + public static final int MAP_ITEM = USHORT + USHORT + UINT + UINT; - /** - * start_addr uint - * insn_count ushort - * handler_off ushort - */ - public static final int TRY_ITEM = UINT + USHORT + USHORT; + /** + * start_addr uint + * insn_count ushort + * handler_off ushort + */ + public static final int TRY_ITEM = UINT + USHORT + USHORT; } diff --git a/dx/src/com/android/jack/dx/dex/TableOfContents.java b/dx/src/com/android/jack/dx/dex/TableOfContents.java index 11b7bfe..40f6e98 100644 --- a/dx/src/com/android/jack/dx/dex/TableOfContents.java +++ b/dx/src/com/android/jack/dx/dex/TableOfContents.java @@ -28,212 +28,230 @@ import java.util.Arrays; */ public final class TableOfContents { - /* - * TODO: factor out ID constants. - */ - - public final Section header = new Section(0x0000); - public final Section stringIds = new Section(0x0001); - public final Section typeIds = new Section(0x0002); - public final Section protoIds = new Section(0x0003); - public final Section fieldIds = new Section(0x0004); - public final Section methodIds = new Section(0x0005); - public final Section classDefs = new Section(0x0006); - public final Section mapList = new Section(0x1000); - public final Section typeLists = new Section(0x1001); - public final Section annotationSetRefLists = new Section(0x1002); - public final Section annotationSets = new Section(0x1003); - public final Section classDatas = new Section(0x2000); - public final Section codes = new Section(0x2001); - public final Section stringDatas = new Section(0x2002); - public final Section debugInfos = new Section(0x2003); - public final Section annotations = new Section(0x2004); - public final Section encodedArrays = new Section(0x2005); - public final Section annotationsDirectories = new Section(0x2006); - public final Section[] sections = { - header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, - typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas, - debugInfos, annotations, encodedArrays, annotationsDirectories - }; - - public int checksum; - public byte[] signature; - public int fileSize; - public int linkSize; - public int linkOff; - public int dataSize; - public int dataOff; - - public TableOfContents() { - signature = new byte[20]; + /* + * TODO(dx team): factor out ID constants. + */ + + public final Section header = new Section(0x0000); + public final Section stringIds = new Section(0x0001); + public final Section typeIds = new Section(0x0002); + public final Section protoIds = new Section(0x0003); + public final Section fieldIds = new Section(0x0004); + public final Section methodIds = new Section(0x0005); + public final Section classDefs = new Section(0x0006); + public final Section mapList = new Section(0x1000); + public final Section typeLists = new Section(0x1001); + public final Section annotationSetRefLists = new Section(0x1002); + public final Section annotationSets = new Section(0x1003); + public final Section classDatas = new Section(0x2000); + public final Section codes = new Section(0x2001); + public final Section stringDatas = new Section(0x2002); + public final Section debugInfos = new Section(0x2003); + public final Section annotations = new Section(0x2004); + public final Section encodedArrays = new Section(0x2005); + public final Section annotationsDirectories = new Section(0x2006); + public final Section[] sections = {header, + stringIds, + typeIds, + protoIds, + fieldIds, + methodIds, + classDefs, + mapList, + typeLists, + annotationSetRefLists, + annotationSets, + classDatas, + codes, + stringDatas, + debugInfos, + annotations, + encodedArrays, + annotationsDirectories}; + + public int checksum; + public byte[] signature; + public int fileSize; + public int linkSize; + public int linkOff; + public int dataSize; + public int dataOff; + + public TableOfContents() { + signature = new byte[20]; + } + + public void readFrom(DexBuffer buffer) throws IOException { + readHeader(buffer.open(0)); + readMap(buffer.open(mapList.off)); + computeSizesFromOffsets(); + } + + private void readHeader(DexBuffer.Section headerIn) throws UnsupportedEncodingException { + byte[] magic = headerIn.readByteArray(8); + int apiTarget = DexFormat.magicToApi(magic); + + if (apiTarget != DexFormat.API_NO_EXTENDED_OPCODES) { + throw new DexException("Unexpected magic: " + Arrays.toString(magic)); + } + + checksum = headerIn.readInt(); + signature = headerIn.readByteArray(20); + fileSize = headerIn.readInt(); + int headerSize = headerIn.readInt(); + if (headerSize != SizeOf.HEADER_ITEM) { + throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize)); + } + int endianTag = headerIn.readInt(); + if (endianTag != DexFormat.ENDIAN_TAG) { + throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag)); } + linkSize = headerIn.readInt(); + linkOff = headerIn.readInt(); + mapList.off = headerIn.readInt(); + if (mapList.off == 0) { + throw new DexException("Cannot merge dex files that do not contain a map"); + } + stringIds.size = headerIn.readInt(); + stringIds.off = headerIn.readInt(); + typeIds.size = headerIn.readInt(); + typeIds.off = headerIn.readInt(); + protoIds.size = headerIn.readInt(); + protoIds.off = headerIn.readInt(); + fieldIds.size = headerIn.readInt(); + fieldIds.off = headerIn.readInt(); + methodIds.size = headerIn.readInt(); + methodIds.off = headerIn.readInt(); + classDefs.size = headerIn.readInt(); + classDefs.off = headerIn.readInt(); + dataSize = headerIn.readInt(); + dataOff = headerIn.readInt(); + } + + private void readMap(DexBuffer.Section in) throws IOException { + int mapSize = in.readInt(); + Section previous = null; + for (int i = 0; i < mapSize; i++) { + short type = in.readShort(); + in.readShort(); // unused + Section section = getSection(type); + int size = in.readInt(); + int offset = in.readInt(); + + if ((section.size != 0 && section.size != size) + || (section.off != -1 && section.off != offset)) { + throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type)); + } + + section.size = size; + section.off = offset; + + if (previous != null && previous.off > section.off) { + throw new DexException("Map is unsorted at " + previous + ", " + section); + } - public void readFrom(DexBuffer buffer) throws IOException { - readHeader(buffer.open(0)); - readMap(buffer.open(mapList.off)); - computeSizesFromOffsets(); + previous = section; } + Arrays.sort(sections); + } - private void readHeader(DexBuffer.Section headerIn) throws UnsupportedEncodingException { - byte[] magic = headerIn.readByteArray(8); - int apiTarget = DexFormat.magicToApi(magic); - - if (apiTarget != DexFormat.API_NO_EXTENDED_OPCODES) { - throw new DexException("Unexpected magic: " + Arrays.toString(magic)); - } - - checksum = headerIn.readInt(); - signature = headerIn.readByteArray(20); - fileSize = headerIn.readInt(); - int headerSize = headerIn.readInt(); - if (headerSize != SizeOf.HEADER_ITEM) { - throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize)); - } - int endianTag = headerIn.readInt(); - if (endianTag != DexFormat.ENDIAN_TAG) { - throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag)); - } - linkSize = headerIn.readInt(); - linkOff = headerIn.readInt(); - mapList.off = headerIn.readInt(); - if (mapList.off == 0) { - throw new DexException("Cannot merge dex files that do not contain a map"); - } - stringIds.size = headerIn.readInt(); - stringIds.off = headerIn.readInt(); - typeIds.size = headerIn.readInt(); - typeIds.off = headerIn.readInt(); - protoIds.size = headerIn.readInt(); - protoIds.off = headerIn.readInt(); - fieldIds.size = headerIn.readInt(); - fieldIds.off = headerIn.readInt(); - methodIds.size = headerIn.readInt(); - methodIds.off = headerIn.readInt(); - classDefs.size = headerIn.readInt(); - classDefs.off = headerIn.readInt(); - dataSize = headerIn.readInt(); - dataOff = headerIn.readInt(); + public void computeSizesFromOffsets() { + int end = dataOff + dataSize; + for (int i = sections.length - 1; i >= 0; i--) { + Section section = sections[i]; + if (section.off == -1) { + continue; + } + if (section.off > end) { + throw new DexException("Map is unsorted at " + section); + } + section.byteCount = end - section.off; + end = section.off; } + } - private void readMap(DexBuffer.Section in) throws IOException { - int mapSize = in.readInt(); - Section previous = null; - for (int i = 0; i < mapSize; i++) { - short type = in.readShort(); - in.readShort(); // unused - Section section = getSection(type); - int size = in.readInt(); - int offset = in.readInt(); - - if ((section.size != 0 && section.size != size) - || (section.off != -1 && section.off != offset)) { - throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type)); - } - - section.size = size; - section.off = offset; - - if (previous != null && previous.off > section.off) { - throw new DexException("Map is unsorted at " + previous + ", " + section); - } - - previous = section; - } - Arrays.sort(sections); + private Section getSection(short type) { + for (Section section : sections) { + if (section.type == type) { + return section; + } } + throw new IllegalArgumentException("No such map item: " + type); + } - public void computeSizesFromOffsets() { - int end = dataOff + dataSize; - for (int i = sections.length - 1; i >= 0; i--) { - Section section = sections[i]; - if (section.off == -1) { - continue; - } - if (section.off > end) { - throw new DexException("Map is unsorted at " + section); - } - section.byteCount = end - section.off; - end = section.off; - } + public void writeHeader(DexBuffer.Section out) throws IOException { + out.write(DexFormat.apiToMagic(DexFormat.API_NO_EXTENDED_OPCODES).getBytes("UTF-8")); + out.writeInt(checksum); + out.write(signature); + out.writeInt(fileSize); + out.writeInt(SizeOf.HEADER_ITEM); + out.writeInt(DexFormat.ENDIAN_TAG); + out.writeInt(linkSize); + out.writeInt(linkOff); + out.writeInt(mapList.off); + out.writeInt(stringIds.size); + out.writeInt(stringIds.off); + out.writeInt(typeIds.size); + out.writeInt(typeIds.off); + out.writeInt(protoIds.size); + out.writeInt(protoIds.off); + out.writeInt(fieldIds.size); + out.writeInt(fieldIds.off); + out.writeInt(methodIds.size); + out.writeInt(methodIds.off); + out.writeInt(classDefs.size); + out.writeInt(classDefs.off); + out.writeInt(dataSize); + out.writeInt(dataOff); + } + + public void writeMap(DexBuffer.Section out) throws IOException { + int count = 0; + for (Section section : sections) { + if (section.exists()) { + count++; + } + } + + out.writeInt(count); + for (Section section : sections) { + if (section.exists()) { + out.writeShort(section.type); + out.writeShort((short) 0); + out.writeInt(section.size); + out.writeInt(section.off); + } } + } + + /** + * TODO(jack team) + */ + public static class Section implements Comparable<Section> { + public final short type; + public int size = 0; + public int off = -1; + public int byteCount = 0; - private Section getSection(short type) { - for (Section section : sections) { - if (section.type == type) { - return section; - } - } - throw new IllegalArgumentException("No such map item: " + type); + public Section(int type) { + this.type = (short) type; } - public void writeHeader(DexBuffer.Section out) throws IOException { - out.write(DexFormat.apiToMagic(DexFormat.API_NO_EXTENDED_OPCODES).getBytes("UTF-8")); - out.writeInt(checksum); - out.write(signature); - out.writeInt(fileSize); - out.writeInt(SizeOf.HEADER_ITEM); - out.writeInt(DexFormat.ENDIAN_TAG); - out.writeInt(linkSize); - out.writeInt(linkOff); - out.writeInt(mapList.off); - out.writeInt(stringIds.size); - out.writeInt(stringIds.off); - out.writeInt(typeIds.size); - out.writeInt(typeIds.off); - out.writeInt(protoIds.size); - out.writeInt(protoIds.off); - out.writeInt(fieldIds.size); - out.writeInt(fieldIds.off); - out.writeInt(methodIds.size); - out.writeInt(methodIds.off); - out.writeInt(classDefs.size); - out.writeInt(classDefs.off); - out.writeInt(dataSize); - out.writeInt(dataOff); + public boolean exists() { + return size > 0; } - public void writeMap(DexBuffer.Section out) throws IOException { - int count = 0; - for (Section section : sections) { - if (section.exists()) { - count++; - } - } - - out.writeInt(count); - for (Section section : sections) { - if (section.exists()) { - out.writeShort(section.type); - out.writeShort((short) 0); - out.writeInt(section.size); - out.writeInt(section.off); - } - } + @Override + public int compareTo(Section section) { + if (off != section.off) { + return off < section.off ? -1 : 1; + } + return 0; } - public static class Section implements Comparable<Section> { - public final short type; - public int size = 0; - public int off = -1; - public int byteCount = 0; - - public Section(int type) { - this.type = (short) type; - } - - public boolean exists() { - return size > 0; - } - - public int compareTo(Section section) { - if (off != section.off) { - return off < section.off ? -1 : 1; - } - return 0; - } - - @Override public String toString() { - return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size); - } + @Override + public String toString() { + return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size); } + } } diff --git a/dx/src/com/android/jack/dx/dex/code/ArrayData.java b/dx/src/com/android/jack/dx/dex/code/ArrayData.java index f8b7684..81f7ac0 100644 --- a/dx/src/com/android/jack/dx/dex/code/ArrayData.java +++ b/dx/src/com/android/jack/dx/dex/code/ArrayData.java @@ -19,8 +19,10 @@ package com.android.jack.dx.dex.code; import com.android.jack.dx.io.Opcodes; import com.android.jack.dx.rop.code.RegisterSpecList; import com.android.jack.dx.rop.code.SourcePosition; -import com.android.jack.dx.rop.cst.*; -import com.android.jack.dx.rop.type.Type; +import com.android.jack.dx.rop.cst.Constant; +import com.android.jack.dx.rop.cst.CstLiteral32; +import com.android.jack.dx.rop.cst.CstLiteral64; +import com.android.jack.dx.rop.cst.CstType; import com.android.jack.dx.util.AnnotatedOutput; import com.android.jack.dx.util.Hex; @@ -30,171 +32,166 @@ import java.util.ArrayList; * Pseudo-instruction which holds fill array data. */ public final class ArrayData extends VariableSizeInsn { - /** - * {@code non-null;} address representing the instruction that uses this - * instance - */ - private final CodeAddress user; - - /** {@code non-null;} initial values to be filled into an array */ - private final ArrayList<Constant> values; - - /** non-null: type of constant that initializes the array */ - private final Constant arrayType; - - /** Width of the init value element */ - private final int elemWidth; - - /** Length of the init list */ - private final int initLength; - - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - * @param user {@code non-null;} address representing the instruction that - * uses this instance - * @param values {@code non-null;} initial values to be filled into an array - */ - public ArrayData(SourcePosition position, CodeAddress user, - ArrayList<Constant> values, - Constant arrayType) { - super(position, RegisterSpecList.EMPTY); - - if (user == null) { - throw new NullPointerException("user == null"); - } - - if (values == null) { - throw new NullPointerException("values == null"); - } - - int sz = values.size(); - - if (sz <= 0) { - throw new IllegalArgumentException("Illegal number of init values"); - } - - this.arrayType = arrayType; - - if (arrayType == CstType.BYTE_ARRAY || - arrayType == CstType.BOOLEAN_ARRAY) { - elemWidth = 1; - } else if (arrayType == CstType.SHORT_ARRAY || - arrayType == CstType.CHAR_ARRAY) { - elemWidth = 2; - } else if (arrayType == CstType.INT_ARRAY || - arrayType == CstType.FLOAT_ARRAY) { - elemWidth = 4; - } else if (arrayType == CstType.LONG_ARRAY || - arrayType == CstType.DOUBLE_ARRAY) { - elemWidth = 8; - } else { - throw new IllegalArgumentException("Unexpected constant type"); - } - this.user = user; - this.values = values; - initLength = values.size(); + /** + * {@code non-null;} address representing the instruction that uses this + * instance + */ + private final CodeAddress user; + + /** {@code non-null;} initial values to be filled into an array */ + private final ArrayList<Constant> values; + + /** non-null: type of constant that initializes the array */ + private final Constant arrayType; + + /** Width of the init value element */ + private final int elemWidth; + + /** Length of the init list */ + private final int initLength; + + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + * @param user {@code non-null;} address representing the instruction that + * uses this instance + * @param values {@code non-null;} initial values to be filled into an array + */ + public ArrayData(SourcePosition position, CodeAddress user, ArrayList<Constant> values, + Constant arrayType) { + super(position, RegisterSpecList.EMPTY); + + if (user == null) { + throw new NullPointerException("user == null"); } - /** {@inheritDoc} */ - @Override - public int codeSize() { - int sz = initLength; - // Note: the unit here is 16-bit - return 4 + ((sz * elemWidth) + 1) / 2; + if (values == null) { + throw new NullPointerException("values == null"); } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out) { - int sz = values.size(); - - out.writeShort(Opcodes.FILL_ARRAY_DATA_PAYLOAD); - out.writeShort(elemWidth); - out.writeInt(initLength); - - - // For speed reasons, replicate the for loop in each case - switch (elemWidth) { - case 1: { - for (int i = 0; i < sz; i++) { - Constant cst = values.get(i); - out.writeByte((byte) ((CstLiteral32) cst).getIntBits()); - } - break; - } - case 2: { - for (int i = 0; i < sz; i++) { - Constant cst = values.get(i); - out.writeShort((short) ((CstLiteral32) cst).getIntBits()); - } - break; - } - case 4: { - for (int i = 0; i < sz; i++) { - Constant cst = values.get(i); - out.writeInt(((CstLiteral32) cst).getIntBits()); - } - break; - } - case 8: { - for (int i = 0; i < sz; i++) { - Constant cst = values.get(i); - out.writeLong(((CstLiteral64) cst).getLongBits()); - } - break; - } - default: - break; - } + int sz = values.size(); - // Pad one byte to make the size of data table multiples of 16-bits - if (elemWidth == 1 && (sz % 2 != 0)) { - out.writeByte(0x00); - } + if (sz <= 0) { + throw new IllegalArgumentException("Illegal number of init values"); } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new ArrayData(getPosition(), user, values, arrayType); + this.arrayType = arrayType; + + if (arrayType == CstType.BYTE_ARRAY || arrayType == CstType.BOOLEAN_ARRAY) { + elemWidth = 1; + } else if (arrayType == CstType.SHORT_ARRAY || arrayType == CstType.CHAR_ARRAY) { + elemWidth = 2; + } else if (arrayType == CstType.INT_ARRAY || arrayType == CstType.FLOAT_ARRAY) { + elemWidth = 4; + } else if (arrayType == CstType.LONG_ARRAY || arrayType == CstType.DOUBLE_ARRAY) { + elemWidth = 8; + } else { + throw new IllegalArgumentException("Unexpected constant type"); } - - /** {@inheritDoc} */ - @Override - protected String argString() { - StringBuffer sb = new StringBuffer(100); - - int sz = values.size(); + this.user = user; + this.values = values; + initLength = values.size(); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + int sz = initLength; + // Note: the unit here is 16-bit + return 4 + ((sz * elemWidth) + 1) / 2; + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out) { + int sz = values.size(); + + out.writeShort(Opcodes.FILL_ARRAY_DATA_PAYLOAD); + out.writeShort(elemWidth); + out.writeInt(initLength); + + + // For speed reasons, replicate the for loop in each case + switch (elemWidth) { + case 1: { for (int i = 0; i < sz; i++) { - sb.append("\n "); - sb.append(i); - sb.append(": "); - sb.append(values.get(i).toHuman()); + Constant cst = values.get(i); + out.writeByte((byte) ((CstLiteral32) cst).getIntBits()); } + break; + } + case 2: { + for (int i = 0; i < sz; i++) { + Constant cst = values.get(i); + out.writeShort((short) ((CstLiteral32) cst).getIntBits()); + } + break; + } + case 4: { + for (int i = 0; i < sz; i++) { + Constant cst = values.get(i); + out.writeInt(((CstLiteral32) cst).getIntBits()); + } + break; + } + case 8: { + for (int i = 0; i < sz; i++) { + Constant cst = values.get(i); + out.writeLong(((CstLiteral64) cst).getLongBits()); + } + break; + } + default: + break; + } - return sb.toString(); + // Pad one byte to make the size of data table multiples of 16-bits + if (elemWidth == 1 && (sz % 2 != 0)) { + out.writeByte(0x00); + } + } + + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new ArrayData(getPosition(), user, values, arrayType); + } + + /** {@inheritDoc} */ + @Override + protected String argString() { + StringBuffer sb = new StringBuffer(100); + + int sz = values.size(); + for (int i = 0; i < sz; i++) { + sb.append("\n "); + sb.append(i); + sb.append(": "); + sb.append(values.get(i).toHuman()); } - /** {@inheritDoc} */ - @Override - protected String listingString0(boolean noteIndices) { - int baseAddress = user.getAddress(); - StringBuffer sb = new StringBuffer(100); - int sz = values.size(); + return sb.toString(); + } - sb.append("fill-array-data-payload // for fill-array-data @ "); - sb.append(Hex.u2(baseAddress)); + /** {@inheritDoc} */ + @Override + protected String listingString0(boolean noteIndices) { + int baseAddress = user.getAddress(); + StringBuffer sb = new StringBuffer(100); + int sz = values.size(); - for (int i = 0; i < sz; i++) { - sb.append("\n "); - sb.append(i); - sb.append(": "); - sb.append(values.get(i).toHuman()); - } + sb.append("fill-array-data-payload // for fill-array-data @ "); + sb.append(Hex.u2(baseAddress)); - return sb.toString(); + for (int i = 0; i < sz; i++) { + sb.append("\n "); + sb.append(i); + sb.append(": "); + sb.append(values.get(i).toHuman()); } + + return sb.toString(); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/BlockAddresses.java b/dx/src/com/android/jack/dx/dex/code/BlockAddresses.java index 4ec10f8..693c342 100644 --- a/dx/src/com/android/jack/dx/dex/code/BlockAddresses.java +++ b/dx/src/com/android/jack/dx/dex/code/BlockAddresses.java @@ -28,116 +28,116 @@ import com.android.jack.dx.rop.code.SourcePosition; * start address, end address, and last instruction address. */ public final class BlockAddresses { - /** {@code non-null;} array containing addresses for the start of each basic - * block (indexed by basic block label) */ - private final CodeAddress[] starts; - - /** {@code non-null;} array containing addresses for the final instruction - * of each basic block (indexed by basic block label) */ - private final CodeAddress[] lasts; - - /** {@code non-null;} array containing addresses for the end (just past the - * final instruction) of each basic block (indexed by basic block - * label) */ - private final CodeAddress[] ends; - - /** - * Constructs an instance. - * - * @param method {@code non-null;} the method to have block addresses for - */ - public BlockAddresses(RopMethod method) { - BasicBlockList blocks = method.getBlocks(); - int maxLabel = blocks.getMaxLabel(); - - this.starts = new CodeAddress[maxLabel]; - this.lasts = new CodeAddress[maxLabel]; - this.ends = new CodeAddress[maxLabel]; - - setupArrays(method); - } - - /** - * Gets the instance for the start of the given block. - * - * @param block {@code non-null;} the block in question - * @return {@code non-null;} the appropriate instance - */ - public CodeAddress getStart(BasicBlock block) { - return starts[block.getLabel()]; - } - - /** - * Gets the instance for the start of the block with the given label. - * - * @param label {@code non-null;} the label of the block in question - * @return {@code non-null;} the appropriate instance - */ - public CodeAddress getStart(int label) { - return starts[label]; - } - - /** - * Gets the instance for the final instruction of the given block. - * - * @param block {@code non-null;} the block in question - * @return {@code non-null;} the appropriate instance - */ - public CodeAddress getLast(BasicBlock block) { - return lasts[block.getLabel()]; - } - - /** - * Gets the instance for the final instruction of the block with - * the given label. - * - * @param label {@code non-null;} the label of the block in question - * @return {@code non-null;} the appropriate instance - */ - public CodeAddress getLast(int label) { - return lasts[label]; - } - - /** - * Gets the instance for the end (address after the final instruction) - * of the given block. - * - * @param block {@code non-null;} the block in question - * @return {@code non-null;} the appropriate instance - */ - public CodeAddress getEnd(BasicBlock block) { - return ends[block.getLabel()]; - } - - /** - * Gets the instance for the end (address after the final instruction) - * of the block with the given label. - * - * @param label {@code non-null;} the label of the block in question - * @return {@code non-null;} the appropriate instance - */ - public CodeAddress getEnd(int label) { - return ends[label]; - } - - /** - * Sets up the address arrays. - */ - private void setupArrays(RopMethod method) { - BasicBlockList blocks = method.getBlocks(); - int sz = blocks.size(); - - for (int i = 0; i < sz; i++) { - BasicBlock one = blocks.get(i); - int label = one.getLabel(); - Insn insn = one.getInsns().get(0); - - starts[label] = new CodeAddress(insn.getPosition()); - - SourcePosition pos = one.getLastInsn().getPosition(); - - lasts[label] = new CodeAddress(pos); - ends[label] = new CodeAddress(pos); - } + /** {@code non-null;} array containing addresses for the start of each basic + * block (indexed by basic block label) */ + private final CodeAddress[] starts; + + /** {@code non-null;} array containing addresses for the final instruction + * of each basic block (indexed by basic block label) */ + private final CodeAddress[] lasts; + + /** {@code non-null;} array containing addresses for the end (just past the + * final instruction) of each basic block (indexed by basic block + * label) */ + private final CodeAddress[] ends; + + /** + * Constructs an instance. + * + * @param method {@code non-null;} the method to have block addresses for + */ + public BlockAddresses(RopMethod method) { + BasicBlockList blocks = method.getBlocks(); + int maxLabel = blocks.getMaxLabel(); + + this.starts = new CodeAddress[maxLabel]; + this.lasts = new CodeAddress[maxLabel]; + this.ends = new CodeAddress[maxLabel]; + + setupArrays(method); + } + + /** + * Gets the instance for the start of the given block. + * + * @param block {@code non-null;} the block in question + * @return {@code non-null;} the appropriate instance + */ + public CodeAddress getStart(BasicBlock block) { + return starts[block.getLabel()]; + } + + /** + * Gets the instance for the start of the block with the given label. + * + * @param label {@code non-null;} the label of the block in question + * @return {@code non-null;} the appropriate instance + */ + public CodeAddress getStart(int label) { + return starts[label]; + } + + /** + * Gets the instance for the final instruction of the given block. + * + * @param block {@code non-null;} the block in question + * @return {@code non-null;} the appropriate instance + */ + public CodeAddress getLast(BasicBlock block) { + return lasts[block.getLabel()]; + } + + /** + * Gets the instance for the final instruction of the block with + * the given label. + * + * @param label {@code non-null;} the label of the block in question + * @return {@code non-null;} the appropriate instance + */ + public CodeAddress getLast(int label) { + return lasts[label]; + } + + /** + * Gets the instance for the end (address after the final instruction) + * of the given block. + * + * @param block {@code non-null;} the block in question + * @return {@code non-null;} the appropriate instance + */ + public CodeAddress getEnd(BasicBlock block) { + return ends[block.getLabel()]; + } + + /** + * Gets the instance for the end (address after the final instruction) + * of the block with the given label. + * + * @param label {@code non-null;} the label of the block in question + * @return {@code non-null;} the appropriate instance + */ + public CodeAddress getEnd(int label) { + return ends[label]; + } + + /** + * Sets up the address arrays. + */ + private void setupArrays(RopMethod method) { + BasicBlockList blocks = method.getBlocks(); + int sz = blocks.size(); + + for (int i = 0; i < sz; i++) { + BasicBlock one = blocks.get(i); + int label = one.getLabel(); + Insn insn = one.getInsns().get(0); + + starts[label] = new CodeAddress(insn.getPosition()); + + SourcePosition pos = one.getLastInsn().getPosition(); + + lasts[label] = new CodeAddress(pos); + ends[label] = new CodeAddress(pos); } + } } diff --git a/dx/src/com/android/jack/dx/dex/code/CatchBuilder.java b/dx/src/com/android/jack/dx/dex/code/CatchBuilder.java index 9b19792..978fe70 100644 --- a/dx/src/com/android/jack/dx/dex/code/CatchBuilder.java +++ b/dx/src/com/android/jack/dx/dex/code/CatchBuilder.java @@ -24,25 +24,25 @@ import java.util.HashSet; * Interface for the construction of {@link CatchTable} instances. */ public interface CatchBuilder { - /** - * Builds and returns the catch table for this instance. - * - * @return {@code non-null;} the constructed table - */ - public CatchTable build(); + /** + * Builds and returns the catch table for this instance. + * + * @return {@code non-null;} the constructed table + */ + public CatchTable build(); - /** - * Gets whether this instance has any catches at all (either typed - * or catch-all). - * - * @return whether this instance has any catches at all - */ - public boolean hasAnyCatches(); + /** + * Gets whether this instance has any catches at all (either typed + * or catch-all). + * + * @return whether this instance has any catches at all + */ + public boolean hasAnyCatches(); - /** - * Gets the set of catch types associated with this instance. - * - * @return {@code non-null;} the set of catch types - */ - public HashSet<Type> getCatchTypes(); + /** + * Gets the set of catch types associated with this instance. + * + * @return {@code non-null;} the set of catch types + */ + public HashSet<Type> getCatchTypes(); } diff --git a/dx/src/com/android/jack/dx/dex/code/CatchHandlerList.java b/dx/src/com/android/jack/dx/dex/code/CatchHandlerList.java index 1f5ae6d..cfec0ab 100644 --- a/dx/src/com/android/jack/dx/dex/code/CatchHandlerList.java +++ b/dx/src/com/android/jack/dx/dex/code/CatchHandlerList.java @@ -23,216 +23,218 @@ import com.android.jack.dx.util.Hex; /** * Ordered list of (exception type, handler address) entries. */ -public final class CatchHandlerList extends FixedSizeList - implements Comparable<CatchHandlerList> { - /** {@code non-null;} empty instance */ - public static final CatchHandlerList EMPTY = new CatchHandlerList(0); +public final class CatchHandlerList extends FixedSizeList implements Comparable<CatchHandlerList> { + /** {@code non-null;} empty instance */ + public static final CatchHandlerList EMPTY = new CatchHandlerList(0); + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size {@code >= 0;} the size of the list + */ + public CatchHandlerList(int size) { + super(size); + } + + /** + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index + */ + public Entry get(int n) { + return (Entry) get0(n); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return toHuman("", ""); + } + + /** + * Get the human form of this instance, prefixed on each line + * with the string. + * + * @param prefix {@code non-null;} the prefix for every line + * @param header {@code non-null;} the header for the first line (after the + * first prefix) + * @return {@code non-null;} the human form + */ + public String toHuman(String prefix, String header) { + StringBuilder sb = new StringBuilder(100); + int size = size(); + + sb.append(prefix); + sb.append(header); + sb.append("catch "); + + for (int i = 0; i < size; i++) { + Entry entry = get(i); + + if (i != 0) { + sb.append(",\n"); + sb.append(prefix); + sb.append(" "); + } - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size {@code >= 0;} the size of the list - */ - public CatchHandlerList(int size) { - super(size); - } + if ((i == (size - 1)) && catchesAll()) { + sb.append("<any>"); + } else { + sb.append(entry.getExceptionType().toHuman()); + } - /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index - */ - public Entry get(int n) { - return (Entry) get0(n); + sb.append(" -> "); + sb.append(Hex.u2or4(entry.getHandler())); } - /** {@inheritDoc} */ - public String toHuman() { - return toHuman("", ""); + return sb.toString(); + } + + /** + * Returns whether or not this instance ends with a "catch-all" + * handler. + * + * @return {@code true} if this instance ends with a "catch-all" + * handler or {@code false} if not + */ + public boolean catchesAll() { + int size = size(); + + if (size == 0) { + return false; } - /** - * Get the human form of this instance, prefixed on each line - * with the string. - * - * @param prefix {@code non-null;} the prefix for every line - * @param header {@code non-null;} the header for the first line (after the - * first prefix) - * @return {@code non-null;} the human form - */ - public String toHuman(String prefix, String header) { - StringBuilder sb = new StringBuilder(100); - int size = size(); - - sb.append(prefix); - sb.append(header); - sb.append("catch "); + Entry last = get(size - 1); + return last.getExceptionType().equals(CstType.OBJECT); + } + + /** + * Sets the entry at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param exceptionType {@code non-null;} type of exception handled + * @param handler {@code >= 0;} exception handler address + */ + public void set(int n, CstType exceptionType, int handler) { + set0(n, new Entry(exceptionType, handler)); + } + + /** + * Sets the entry at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param entry {@code non-null;} the entry to set at {@code n} + */ + public void set(int n, Entry entry) { + set0(n, entry); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(CatchHandlerList other) { + if (this == other) { + // Easy out. + return 0; + } - for (int i = 0; i < size; i++) { - Entry entry = get(i); + int thisSize = size(); + int otherSize = other.size(); + int checkSize = Math.min(thisSize, otherSize); + + for (int i = 0; i < checkSize; i++) { + Entry thisEntry = get(i); + Entry otherEntry = other.get(i); + int compare = thisEntry.compareTo(otherEntry); + if (compare != 0) { + return compare; + } + } - if (i != 0) { - sb.append(",\n"); - sb.append(prefix); - sb.append(" "); - } + if (thisSize < otherSize) { + return -1; + } else if (thisSize > otherSize) { + return 1; + } - if ((i == (size - 1)) && catchesAll()) { - sb.append("<any>"); - } else { - sb.append(entry.getExceptionType().toHuman()); - } + return 0; + } - sb.append(" -> "); - sb.append(Hex.u2or4(entry.getHandler())); - } + /** + * Entry in the list. + */ + public static class Entry implements Comparable<Entry> { + /** {@code non-null;} type of exception handled */ + private final CstType exceptionType; - return sb.toString(); - } + /** {@code >= 0;} exception handler address */ + private final int handler; /** - * Returns whether or not this instance ends with a "catch-all" - * handler. + * Constructs an instance. * - * @return {@code true} if this instance ends with a "catch-all" - * handler or {@code false} if not + * @param exceptionType {@code non-null;} type of exception handled + * @param handler {@code >= 0;} exception handler address */ - public boolean catchesAll() { - int size = size(); + public Entry(CstType exceptionType, int handler) { + if (handler < 0) { + throw new IllegalArgumentException("handler < 0"); + } - if (size == 0) { - return false; - } + if (exceptionType == null) { + throw new NullPointerException("exceptionType == null"); + } - Entry last = get(size - 1); - return last.getExceptionType().equals(CstType.OBJECT); + this.handler = handler; + this.exceptionType = exceptionType; } - /** - * Sets the entry at the given index. - * - * @param n {@code >= 0, < size();} which index - * @param exceptionType {@code non-null;} type of exception handled - * @param handler {@code >= 0;} exception handler address - */ - public void set(int n, CstType exceptionType, int handler) { - set0(n, new Entry(exceptionType, handler)); + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (handler * 31) + exceptionType.hashCode(); } - /** - * Sets the entry at the given index. - * - * @param n {@code >= 0, < size();} which index - * @param entry {@code non-null;} the entry to set at {@code n} - */ - public void set(int n, Entry entry) { - set0(n, entry); + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (other instanceof Entry) { + return (compareTo((Entry) other) == 0); + } + + return false; } /** {@inheritDoc} */ - public int compareTo(CatchHandlerList other) { - if (this == other) { - // Easy out. - return 0; - } - - int thisSize = size(); - int otherSize = other.size(); - int checkSize = Math.min(thisSize, otherSize); - - for (int i = 0; i < checkSize; i++) { - Entry thisEntry = get(i); - Entry otherEntry = other.get(i); - int compare = thisEntry.compareTo(otherEntry); - if (compare != 0) { - return compare; - } - } - - if (thisSize < otherSize) { - return -1; - } else if (thisSize > otherSize) { - return 1; - } - - return 0; + @Override + public int compareTo(Entry other) { + if (handler < other.handler) { + return -1; + } else if (handler > other.handler) { + return 1; + } + + return exceptionType.compareTo(other.exceptionType); } /** - * Entry in the list. + * Gets the exception type handled. + * + * @return {@code non-null;} the exception type + */ + public CstType getExceptionType() { + return exceptionType; + } + + /** + * Gets the handler address. + * + * @return {@code >= 0;} the handler address */ - public static class Entry implements Comparable<Entry> { - /** {@code non-null;} type of exception handled */ - private final CstType exceptionType; - - /** {@code >= 0;} exception handler address */ - private final int handler; - - /** - * Constructs an instance. - * - * @param exceptionType {@code non-null;} type of exception handled - * @param handler {@code >= 0;} exception handler address - */ - public Entry(CstType exceptionType, int handler) { - if (handler < 0) { - throw new IllegalArgumentException("handler < 0"); - } - - if (exceptionType == null) { - throw new NullPointerException("exceptionType == null"); - } - - this.handler = handler; - this.exceptionType = exceptionType; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (handler * 31) + exceptionType.hashCode(); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (other instanceof Entry) { - return (compareTo((Entry) other) == 0); - } - - return false; - } - - /** {@inheritDoc} */ - public int compareTo(Entry other) { - if (handler < other.handler) { - return -1; - } else if (handler > other.handler) { - return 1; - } - - return exceptionType.compareTo(other.exceptionType); - } - - /** - * Gets the exception type handled. - * - * @return {@code non-null;} the exception type - */ - public CstType getExceptionType() { - return exceptionType; - } - - /** - * Gets the handler address. - * - * @return {@code >= 0;} the handler address - */ - public int getHandler() { - return handler; - } + public int getHandler() { + return handler; } + } } diff --git a/dx/src/com/android/jack/dx/dex/code/CatchTable.java b/dx/src/com/android/jack/dx/dex/code/CatchTable.java index 6e10e8a..54fc432 100644 --- a/dx/src/com/android/jack/dx/dex/code/CatchTable.java +++ b/dx/src/com/android/jack/dx/dex/code/CatchTable.java @@ -16,7 +16,6 @@ package com.android.jack.dx.dex.code; -import com.android.jack.dx.rop.cst.CstType; import com.android.jack.dx.util.FixedSizeList; /** @@ -24,169 +23,170 @@ import com.android.jack.dx.util.FixedSizeList; * addresses for which it is valid and an associated {@link * CatchHandlerList}. */ -public final class CatchTable extends FixedSizeList - implements Comparable<CatchTable> { - /** {@code non-null;} empty instance */ - public static final CatchTable EMPTY = new CatchTable(0); +public final class CatchTable extends FixedSizeList implements Comparable<CatchTable> { + /** {@code non-null;} empty instance */ + public static final CatchTable EMPTY = new CatchTable(0); + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size {@code >= 0;} the size of the table + */ + public CatchTable(int size) { + super(size); + } + + /** + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index + */ + public Entry get(int n) { + return (Entry) get0(n); + } + + /** + * Sets the entry at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param entry {@code non-null;} the entry to set at {@code n} + */ + public void set(int n, Entry entry) { + set0(n, entry); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(CatchTable other) { + if (this == other) { + // Easy out. + return 0; + } + + int thisSize = size(); + int otherSize = other.size(); + int checkSize = Math.min(thisSize, otherSize); + + for (int i = 0; i < checkSize; i++) { + Entry thisEntry = get(i); + Entry otherEntry = other.get(i); + int compare = thisEntry.compareTo(otherEntry); + if (compare != 0) { + return compare; + } + } + + if (thisSize < otherSize) { + return -1; + } else if (thisSize > otherSize) { + return 1; + } + + return 0; + } + + /** + * Entry in a catch list. + */ + public static class Entry implements Comparable<Entry> { + /** {@code >= 0;} start address */ + private final int start; + + /** {@code > start;} end address (exclusive) */ + private final int end; + + /** {@code non-null;} list of catch handlers */ + private final CatchHandlerList handlers; /** - * Constructs an instance. All indices initially contain {@code null}. + * Constructs an instance. * - * @param size {@code >= 0;} the size of the table + * @param start {@code >= 0;} start address + * @param end {@code > start;} end address (exclusive) + * @param handlers {@code non-null;} list of catch handlers */ - public CatchTable(int size) { - super(size); + public Entry(int start, int end, CatchHandlerList handlers) { + if (start < 0) { + throw new IllegalArgumentException("start < 0"); + } + + if (end <= start) { + throw new IllegalArgumentException("end <= start"); + } + + if (handlers.isMutable()) { + throw new IllegalArgumentException("handlers.isMutable()"); + } + + this.start = start; + this.end = end; + this.handlers = handlers; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + int hash = (start * 31) + end; + hash = (hash * 31) + handlers.hashCode(); + return hash; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (other instanceof Entry) { + return (compareTo((Entry) other) == 0); + } + + return false; + } + + /** {@inheritDoc} */ + @Override + public int compareTo(Entry other) { + if (start < other.start) { + return -1; + } else if (start > other.start) { + return 1; + } + + if (end < other.end) { + return -1; + } else if (end > other.end) { + return 1; + } + + return handlers.compareTo(other.handlers); } /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. + * Gets the start address. * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index + * @return {@code >= 0;} the start address */ - public Entry get(int n) { - return (Entry) get0(n); + public int getStart() { + return start; } /** - * Sets the entry at the given index. + * Gets the end address (exclusive). * - * @param n {@code >= 0, < size();} which index - * @param entry {@code non-null;} the entry to set at {@code n} + * @return {@code > start;} the end address (exclusive) */ - public void set(int n, Entry entry) { - set0(n, entry); - } - - /** {@inheritDoc} */ - public int compareTo(CatchTable other) { - if (this == other) { - // Easy out. - return 0; - } - - int thisSize = size(); - int otherSize = other.size(); - int checkSize = Math.min(thisSize, otherSize); - - for (int i = 0; i < checkSize; i++) { - Entry thisEntry = get(i); - Entry otherEntry = other.get(i); - int compare = thisEntry.compareTo(otherEntry); - if (compare != 0) { - return compare; - } - } - - if (thisSize < otherSize) { - return -1; - } else if (thisSize > otherSize) { - return 1; - } - - return 0; + public int getEnd() { + return end; } /** - * Entry in a catch list. + * Gets the handlers. + * + * @return {@code non-null;} the handlers */ - public static class Entry implements Comparable<Entry> { - /** {@code >= 0;} start address */ - private final int start; - - /** {@code > start;} end address (exclusive) */ - private final int end; - - /** {@code non-null;} list of catch handlers */ - private final CatchHandlerList handlers; - - /** - * Constructs an instance. - * - * @param start {@code >= 0;} start address - * @param end {@code > start;} end address (exclusive) - * @param handlers {@code non-null;} list of catch handlers - */ - public Entry(int start, int end, CatchHandlerList handlers) { - if (start < 0) { - throw new IllegalArgumentException("start < 0"); - } - - if (end <= start) { - throw new IllegalArgumentException("end <= start"); - } - - if (handlers.isMutable()) { - throw new IllegalArgumentException("handlers.isMutable()"); - } - - this.start = start; - this.end = end; - this.handlers = handlers; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - int hash = (start * 31) + end; - hash = (hash * 31) + handlers.hashCode(); - return hash; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (other instanceof Entry) { - return (compareTo((Entry) other) == 0); - } - - return false; - } - - /** {@inheritDoc} */ - public int compareTo(Entry other) { - if (start < other.start) { - return -1; - } else if (start > other.start) { - return 1; - } - - if (end < other.end) { - return -1; - } else if (end > other.end) { - return 1; - } - - return handlers.compareTo(other.handlers); - } - - /** - * Gets the start address. - * - * @return {@code >= 0;} the start address - */ - public int getStart() { - return start; - } - - /** - * Gets the end address (exclusive). - * - * @return {@code > start;} the end address (exclusive) - */ - public int getEnd() { - return end; - } - - /** - * Gets the handlers. - * - * @return {@code non-null;} the handlers - */ - public CatchHandlerList getHandlers() { - return handlers; - } + public CatchHandlerList getHandlers() { + return handlers; } + } } diff --git a/dx/src/com/android/jack/dx/dex/code/CodeAddress.java b/dx/src/com/android/jack/dx/dex/code/CodeAddress.java index bb4904d..28a3c9a 100644 --- a/dx/src/com/android/jack/dx/dex/code/CodeAddress.java +++ b/dx/src/com/android/jack/dx/dex/code/CodeAddress.java @@ -27,65 +27,65 @@ import com.android.jack.dx.rop.code.SourcePosition; * human-oriented or binary file). */ public final class CodeAddress extends ZeroSizeInsn { - /** If this address should bind closely to the following real instruction */ - private final boolean bindsClosely; + /** If this address should bind closely to the following real instruction */ + private final boolean bindsClosely; - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - */ - public CodeAddress(SourcePosition position) { - this(position, false); - } + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + */ + public CodeAddress(SourcePosition position) { + this(position, false); + } - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - * @param bindsClosely if the address should bind closely to the following - * real instruction. - */ - public CodeAddress(SourcePosition position, boolean bindsClosely) { - super(position); - this.bindsClosely = bindsClosely; - } + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + * @param bindsClosely if the address should bind closely to the following + * real instruction. + */ + public CodeAddress(SourcePosition position, boolean bindsClosely) { + super(position); + this.bindsClosely = bindsClosely; + } - /** {@inheritDoc} */ - @Override - public final DalvInsn withRegisters(RegisterSpecList registers) { - return new CodeAddress(getPosition()); - } + /** {@inheritDoc} */ + @Override + public final DalvInsn withRegisters(RegisterSpecList registers) { + return new CodeAddress(getPosition()); + } - /** {@inheritDoc} */ - @Override - protected String argString() { - return null; - } + /** {@inheritDoc} */ + @Override + protected String argString() { + return null; + } - /** {@inheritDoc} */ - @Override - protected String listingString0(boolean noteIndices) { - return "code-address"; - } + /** {@inheritDoc} */ + @Override + protected String listingString0(boolean noteIndices) { + return "code-address"; + } - /** - * Gets whether this address binds closely to the following "real" - * (non-zero-length) instruction. - * - * When a prefix is added to an instruction (for example, to move a value - * from a high register to a low register), this determines whether this - * {@code CodeAddress} will point to the prefix, or to the instruction - * itself. - * - * If bindsClosely is true, the address will point to the instruction - * itself, otherwise it will point to the prefix (if any) - * - * @return true if this address binds closely to the next real instruction - */ - public boolean getBindsClosely() { - return bindsClosely; - } + /** + * Gets whether this address binds closely to the following "real" + * (non-zero-length) instruction. + * + * When a prefix is added to an instruction (for example, to move a value + * from a high register to a low register), this determines whether this + * {@code CodeAddress} will point to the prefix, or to the instruction + * itself. + * + * If bindsClosely is true, the address will point to the instruction + * itself, otherwise it will point to the prefix (if any) + * + * @return true if this address binds closely to the next real instruction + */ + public boolean getBindsClosely() { + return bindsClosely; + } } diff --git a/dx/src/com/android/jack/dx/dex/code/CstInsn.java b/dx/src/com/android/jack/dx/dex/code/CstInsn.java index a83e485..bf3cdb2 100644 --- a/dx/src/com/android/jack/dx/dex/code/CstInsn.java +++ b/dx/src/com/android/jack/dx/dex/code/CstInsn.java @@ -25,181 +25,179 @@ import com.android.jack.dx.rop.cst.Constant; * to all the normal instruction information. */ public final class CstInsn extends FixedSizeInsn { - /** {@code non-null;} the constant argument for this instruction */ - private final Constant constant; - - /** - * {@code >= -1;} the constant pool index for {@link #constant}, or - * {@code -1} if not yet set - */ - private int index; - - /** - * {@code >= -1;} the constant pool index for the class reference in - * {@link #constant} if any, or {@code -1} if not yet set - */ - private int classIndex; - - /** - * Constructs an instance. The output address of this instance is - * initially unknown ({@code -1}) as is the constant pool index. - * - * @param opcode the opcode; one of the constants from {@link Dops} - * @param position {@code non-null;} source position - * @param registers {@code non-null;} register list, including a - * result register if appropriate (that is, registers may be either - * ins or outs) - * @param constant {@code non-null;} constant argument - */ - public CstInsn(Dop opcode, SourcePosition position, - RegisterSpecList registers, Constant constant) { - super(opcode, position, registers); - - if (constant == null) { - throw new NullPointerException("constant == null"); - } - - this.constant = constant; - this.index = -1; - this.classIndex = -1; + /** {@code non-null;} the constant argument for this instruction */ + private final Constant constant; + + /** + * {@code >= -1;} the constant pool index for {@link #constant}, or + * {@code -1} if not yet set + */ + private int index; + + /** + * {@code >= -1;} the constant pool index for the class reference in + * {@link #constant} if any, or {@code -1} if not yet set + */ + private int classIndex; + + /** + * Constructs an instance. The output address of this instance is + * initially unknown ({@code -1}) as is the constant pool index. + * + * @param opcode the opcode; one of the constants from {@link Dops} + * @param position {@code non-null;} source position + * @param registers {@code non-null;} register list, including a + * result register if appropriate (that is, registers may be either + * ins or outs) + * @param constant {@code non-null;} constant argument + */ + public CstInsn(Dop opcode, SourcePosition position, RegisterSpecList registers, + Constant constant) { + super(opcode, position, registers); + + if (constant == null) { + throw new NullPointerException("constant == null"); } - /** {@inheritDoc} */ - @Override - public DalvInsn withOpcode(Dop opcode) { - CstInsn result = - new CstInsn(opcode, getPosition(), getRegisters(), constant); + this.constant = constant; + this.index = -1; + this.classIndex = -1; + } - if (index >= 0) { - result.setIndex(index); - } + /** {@inheritDoc} */ + @Override + public DalvInsn withOpcode(Dop opcode) { + CstInsn result = new CstInsn(opcode, getPosition(), getRegisters(), constant); - if (classIndex >= 0) { - result.setClassIndex(classIndex); - } - - return result; + if (index >= 0) { + result.setIndex(index); } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - CstInsn result = - new CstInsn(getOpcode(), getPosition(), registers, constant); + if (classIndex >= 0) { + result.setClassIndex(classIndex); + } - if (index >= 0) { - result.setIndex(index); - } + return result; + } - if (classIndex >= 0) { - result.setClassIndex(classIndex); - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + CstInsn result = new CstInsn(getOpcode(), getPosition(), registers, constant); - return result; + if (index >= 0) { + result.setIndex(index); } - /** - * Gets the constant argument. - * - * @return {@code non-null;} the constant argument - */ - public Constant getConstant() { - return constant; + if (classIndex >= 0) { + result.setClassIndex(classIndex); } - /** - * Gets the constant's index. It is only valid to call this after - * {@link #setIndex} has been called. - * - * @return {@code >= 0;} the constant pool index - */ - public int getIndex() { - if (index < 0) { - throw new RuntimeException("index not yet set for " + constant); - } - - return index; + return result; + } + + /** + * Gets the constant argument. + * + * @return {@code non-null;} the constant argument + */ + public Constant getConstant() { + return constant; + } + + /** + * Gets the constant's index. It is only valid to call this after + * {@link #setIndex} has been called. + * + * @return {@code >= 0;} the constant pool index + */ + public int getIndex() { + if (index < 0) { + throw new RuntimeException("index not yet set for " + constant); } - /** - * Returns whether the constant's index has been set for this instance. - * - * @see #setIndex - * - * @return {@code true} iff the index has been set - */ - public boolean hasIndex() { - return (index >= 0); + return index; + } + + /** + * Returns whether the constant's index has been set for this instance. + * + * @see #setIndex + * + * @return {@code true} iff the index has been set + */ + public boolean hasIndex() { + return (index >= 0); + } + + /** + * Sets the constant's index. It is only valid to call this method once + * per instance. + * + * @param index {@code >= 0;} the constant pool index + */ + public void setIndex(int index) { + if (index < 0) { + throw new IllegalArgumentException("index < 0"); } - /** - * Sets the constant's index. It is only valid to call this method once - * per instance. - * - * @param index {@code >= 0;} the constant pool index - */ - public void setIndex(int index) { - if (index < 0) { - throw new IllegalArgumentException("index < 0"); - } - - if (this.index >= 0) { - throw new RuntimeException("index already set"); - } - - this.index = index; + if (this.index >= 0) { + throw new RuntimeException("index already set"); } - /** - * Gets the constant's class index. It is only valid to call this after - * {@link #setClassIndex} has been called. - * - * @return {@code >= 0;} the constant's class's constant pool index - */ - public int getClassIndex() { - if (classIndex < 0) { - throw new RuntimeException("class index not yet set"); - } - - return classIndex; + this.index = index; + } + + /** + * Gets the constant's class index. It is only valid to call this after + * {@link #setClassIndex} has been called. + * + * @return {@code >= 0;} the constant's class's constant pool index + */ + public int getClassIndex() { + if (classIndex < 0) { + throw new RuntimeException("class index not yet set"); } - /** - * Returns whether the constant's class index has been set for this - * instance. - * - * @see #setClassIndex - * - * @return {@code true} iff the index has been set - */ - public boolean hasClassIndex() { - return (classIndex >= 0); + return classIndex; + } + + /** + * Returns whether the constant's class index has been set for this + * instance. + * + * @see #setClassIndex + * + * @return {@code true} iff the index has been set + */ + public boolean hasClassIndex() { + return (classIndex >= 0); + } + + /** + * Sets the constant's class index. This is the constant pool index + * for the class referred to by this instance's constant. Only + * reference constants have a class, so it is only on instances + * with reference constants that this method should ever be + * called. It is only valid to call this method once per instance. + * + * @param index {@code >= 0;} the constant's class's constant pool index + */ + public void setClassIndex(int index) { + if (index < 0) { + throw new IllegalArgumentException("index < 0"); } - /** - * Sets the constant's class index. This is the constant pool index - * for the class referred to by this instance's constant. Only - * reference constants have a class, so it is only on instances - * with reference constants that this method should ever be - * called. It is only valid to call this method once per instance. - * - * @param index {@code >= 0;} the constant's class's constant pool index - */ - public void setClassIndex(int index) { - if (index < 0) { - throw new IllegalArgumentException("index < 0"); - } - - if (this.classIndex >= 0) { - throw new RuntimeException("class index already set"); - } - - this.classIndex = index; + if (this.classIndex >= 0) { + throw new RuntimeException("class index already set"); } - /** {@inheritDoc} */ - @Override - protected String argString() { - return constant.toHuman(); - } + this.classIndex = index; + } + + /** {@inheritDoc} */ + @Override + protected String argString() { + return constant.toHuman(); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/DalvCode.java b/dx/src/com/android/jack/dx/dex/code/DalvCode.java index e691ef1..bf92ad4 100644 --- a/dx/src/com/android/jack/dx/dex/code/DalvCode.java +++ b/dx/src/com/android/jack/dx/dex/code/DalvCode.java @@ -26,207 +26,206 @@ import java.util.HashSet; * corresponds to a {@code code} structure in a {@code .dex} file. */ public final class DalvCode { - /** - * how much position info to preserve; one of the static - * constants in {@link PositionList} - */ - private final int positionInfo; - - /** - * {@code null-ok;} the instruction list, ready for final processing; - * nulled out in {@link #finishProcessingIfNecessary} - */ - private OutputFinisher unprocessedInsns; - - /** - * {@code non-null;} unprocessed catch table; - * nulled out in {@link #finishProcessingIfNecessary} - */ - private CatchBuilder unprocessedCatches; - - /** - * {@code null-ok;} catch table; set in - * {@link #finishProcessingIfNecessary} - */ - private CatchTable catches; - - /** - * {@code null-ok;} source positions list; set in - * {@link #finishProcessingIfNecessary} - */ - private PositionList positions; - - /** - * {@code null-ok;} local variable list; set in - * {@link #finishProcessingIfNecessary} - */ - private LocalList locals; - - /** - * {@code null-ok;} the processed instruction list; set in - * {@link #finishProcessingIfNecessary} - */ - private DalvInsnList insns; - - /** - * Constructs an instance. - * - * @param positionInfo how much position info to preserve; one of the - * static constants in {@link PositionList} - * @param unprocessedInsns {@code non-null;} the instruction list, ready - * for final processing - * @param unprocessedCatches {@code non-null;} unprocessed catch - * (exception handler) table - */ - public DalvCode(int positionInfo, OutputFinisher unprocessedInsns, - CatchBuilder unprocessedCatches) { - if (unprocessedInsns == null) { - throw new NullPointerException("unprocessedInsns == null"); - } - - if (unprocessedCatches == null) { - throw new NullPointerException("unprocessedCatches == null"); - } - - this.positionInfo = positionInfo; - this.unprocessedInsns = unprocessedInsns; - this.unprocessedCatches = unprocessedCatches; - this.catches = null; - this.positions = null; - this.locals = null; - this.insns = null; + /** + * how much position info to preserve; one of the static + * constants in {@link PositionList} + */ + private final int positionInfo; + + /** + * {@code null-ok;} the instruction list, ready for final processing; + * nulled out in {@link #finishProcessingIfNecessary} + */ + private OutputFinisher unprocessedInsns; + + /** + * {@code non-null;} unprocessed catch table; + * nulled out in {@link #finishProcessingIfNecessary} + */ + private CatchBuilder unprocessedCatches; + + /** + * {@code null-ok;} catch table; set in + * {@link #finishProcessingIfNecessary} + */ + private CatchTable catches; + + /** + * {@code null-ok;} source positions list; set in + * {@link #finishProcessingIfNecessary} + */ + private PositionList positions; + + /** + * {@code null-ok;} local variable list; set in + * {@link #finishProcessingIfNecessary} + */ + private LocalList locals; + + /** + * {@code null-ok;} the processed instruction list; set in + * {@link #finishProcessingIfNecessary} + */ + private DalvInsnList insns; + + /** + * Constructs an instance. + * + * @param positionInfo how much position info to preserve; one of the + * static constants in {@link PositionList} + * @param unprocessedInsns {@code non-null;} the instruction list, ready + * for final processing + * @param unprocessedCatches {@code non-null;} unprocessed catch + * (exception handler) table + */ + public DalvCode(int positionInfo, OutputFinisher unprocessedInsns, + CatchBuilder unprocessedCatches) { + if (unprocessedInsns == null) { + throw new NullPointerException("unprocessedInsns == null"); } - /** - * Finish up processing of the method. - */ - private void finishProcessingIfNecessary() { - if (insns != null) { - return; - } - - insns = unprocessedInsns.finishProcessingAndGetList(); - positions = PositionList.make(insns, positionInfo); - locals = LocalList.make(insns); - catches = unprocessedCatches.build(); - - // Let them be gc'ed. - unprocessedInsns = null; - unprocessedCatches = null; + if (unprocessedCatches == null) { + throw new NullPointerException("unprocessedCatches == null"); } - /** - * Assign indices in all instructions that need them, using the - * given callback to perform lookups. This must be called before - * {@link #getInsns}. - * - * @param callback {@code non-null;} callback object - */ - public void assignIndices(AssignIndicesCallback callback) { - unprocessedInsns.assignIndices(callback); + this.positionInfo = positionInfo; + this.unprocessedInsns = unprocessedInsns; + this.unprocessedCatches = unprocessedCatches; + this.catches = null; + this.positions = null; + this.locals = null; + this.insns = null; + } + + /** + * Finish up processing of the method. + */ + private void finishProcessingIfNecessary() { + if (insns != null) { + return; } - /** - * Gets whether this instance has any position data to represent. + insns = unprocessedInsns.finishProcessingAndGetList(); + positions = PositionList.make(insns, positionInfo); + locals = LocalList.make(insns); + catches = unprocessedCatches.build(); + + // Let them be gc'ed. + unprocessedInsns = null; + unprocessedCatches = null; + } + + /** + * Assign indices in all instructions that need them, using the + * given callback to perform lookups. This must be called before + * {@link #getInsns}. + * + * @param callback {@code non-null;} callback object + */ + public void assignIndices(AssignIndicesCallback callback) { + unprocessedInsns.assignIndices(callback); + } + + /** + * Gets whether this instance has any position data to represent. + * + * @return {@code true} iff this instance has any position + * data to represent + */ + public boolean hasPositions() { + return (positionInfo != PositionList.NONE) && unprocessedInsns.hasAnyPositionInfo(); + } + + /** + * Gets whether this instance has any local variable data to represent. + * + * @return {@code true} iff this instance has any local variable + * data to represent + */ + public boolean hasLocals() { + return unprocessedInsns.hasAnyLocalInfo(); + } + + /** + * Gets whether this instance has any catches at all (either typed + * or catch-all). + * + * @return whether this instance has any catches at all + */ + public boolean hasAnyCatches() { + return unprocessedCatches.hasAnyCatches(); + } + + /** + * Gets the set of catch types handled anywhere in the code. + * + * @return {@code non-null;} the set of catch types + */ + public HashSet<Type> getCatchTypes() { + return unprocessedCatches.getCatchTypes(); + } + + /** + * Gets the set of all constants referred to by instructions in + * the code. + * + * @return {@code non-null;} the set of constants + */ + public HashSet<Constant> getInsnConstants() { + return unprocessedInsns.getAllConstants(); + } + + /** + * Gets the list of instructions. + * + * @return {@code non-null;} the instruction list + */ + public DalvInsnList getInsns() { + finishProcessingIfNecessary(); + return insns; + } + + /** + * Gets the catch (exception handler) table. + * + * @return {@code non-null;} the catch table + */ + public CatchTable getCatches() { + finishProcessingIfNecessary(); + return catches; + } + + /** + * Gets the source positions list. + * + * @return {@code non-null;} the source positions list + */ + public PositionList getPositions() { + finishProcessingIfNecessary(); + return positions; + } + + /** + * Gets the source positions list. + * + * @return {@code non-null;} the source positions list + */ + public LocalList getLocals() { + finishProcessingIfNecessary(); + return locals; + } + + /** + * Class used as a callback for {@link #assignIndices}. + */ + public static interface AssignIndicesCallback { + /** + * Gets the index for the given constant. * - * @return {@code true} iff this instance has any position - * data to represent + * @param cst {@code non-null;} the constant + * @return {@code >= -1;} the index or {@code -1} if the constant + * shouldn't actually be reified with an index */ - public boolean hasPositions() { - return (positionInfo != PositionList.NONE) - && unprocessedInsns.hasAnyPositionInfo(); - } - - /** - * Gets whether this instance has any local variable data to represent. - * - * @return {@code true} iff this instance has any local variable - * data to represent - */ - public boolean hasLocals() { - return unprocessedInsns.hasAnyLocalInfo(); - } - - /** - * Gets whether this instance has any catches at all (either typed - * or catch-all). - * - * @return whether this instance has any catches at all - */ - public boolean hasAnyCatches() { - return unprocessedCatches.hasAnyCatches(); - } - - /** - * Gets the set of catch types handled anywhere in the code. - * - * @return {@code non-null;} the set of catch types - */ - public HashSet<Type> getCatchTypes() { - return unprocessedCatches.getCatchTypes(); - } - - /** - * Gets the set of all constants referred to by instructions in - * the code. - * - * @return {@code non-null;} the set of constants - */ - public HashSet<Constant> getInsnConstants() { - return unprocessedInsns.getAllConstants(); - } - - /** - * Gets the list of instructions. - * - * @return {@code non-null;} the instruction list - */ - public DalvInsnList getInsns() { - finishProcessingIfNecessary(); - return insns; - } - - /** - * Gets the catch (exception handler) table. - * - * @return {@code non-null;} the catch table - */ - public CatchTable getCatches() { - finishProcessingIfNecessary(); - return catches; - } - - /** - * Gets the source positions list. - * - * @return {@code non-null;} the source positions list - */ - public PositionList getPositions() { - finishProcessingIfNecessary(); - return positions; - } - - /** - * Gets the source positions list. - * - * @return {@code non-null;} the source positions list - */ - public LocalList getLocals() { - finishProcessingIfNecessary(); - return locals; - } - - /** - * Class used as a callback for {@link #assignIndices}. - */ - public static interface AssignIndicesCallback { - /** - * Gets the index for the given constant. - * - * @param cst {@code non-null;} the constant - * @return {@code >= -1;} the index or {@code -1} if the constant - * shouldn't actually be reified with an index - */ - public int getIndex(Constant cst); - } + public int getIndex(Constant cst); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/DalvInsn.java b/dx/src/com/android/jack/dx/dex/code/DalvInsn.java index a6a501e..4667d2f 100644 --- a/dx/src/com/android/jack/dx/dex/code/DalvInsn.java +++ b/dx/src/com/android/jack/dx/dex/code/DalvInsn.java @@ -29,422 +29,420 @@ import java.util.BitSet; * Base class for Dalvik instructions. */ public abstract class DalvInsn { - /** - * the actual output address of this instance, if known, or - * {@code -1} if not - */ - private int address; - - /** the opcode; one of the constants from {@link Dops} */ - private final Dop opcode; - - /** {@code non-null;} source position */ - private final SourcePosition position; - - /** {@code non-null;} list of register arguments */ - private final RegisterSpecList registers; - - /** - * Makes a move instruction, appropriate and ideal for the given arguments. - * - * @param position {@code non-null;} source position information - * @param dest {@code non-null;} destination register - * @param src {@code non-null;} source register - * @return {@code non-null;} an appropriately-constructed instance - */ - public static SimpleInsn makeMove(SourcePosition position, - RegisterSpec dest, RegisterSpec src) { - boolean category1 = dest.getCategory() == 1; - boolean reference = dest.getType().isReference(); - int destReg = dest.getReg(); - int srcReg = src.getReg(); - Dop opcode; - - if ((srcReg | destReg) < 16) { - opcode = reference ? Dops.MOVE_OBJECT : - (category1 ? Dops.MOVE : Dops.MOVE_WIDE); - } else if (destReg < 256) { - opcode = reference ? Dops.MOVE_OBJECT_FROM16 : - (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16); - } else { - opcode = reference ? Dops.MOVE_OBJECT_16 : - (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16); - } - - return new SimpleInsn(opcode, position, - RegisterSpecList.make(dest, src)); + /** + * the actual output address of this instance, if known, or + * {@code -1} if not + */ + private int address; + + /** the opcode; one of the constants from {@link Dops} */ + private final Dop opcode; + + /** {@code non-null;} source position */ + private final SourcePosition position; + + /** {@code non-null;} list of register arguments */ + private final RegisterSpecList registers; + + /** + * Makes a move instruction, appropriate and ideal for the given arguments. + * + * @param position {@code non-null;} source position information + * @param dest {@code non-null;} destination register + * @param src {@code non-null;} source register + * @return {@code non-null;} an appropriately-constructed instance + */ + public static SimpleInsn makeMove(SourcePosition position, RegisterSpec dest, RegisterSpec src) { + boolean category1 = dest.getCategory() == 1; + boolean reference = dest.getType().isReference(); + int destReg = dest.getReg(); + int srcReg = src.getReg(); + Dop opcode; + + if ((srcReg | destReg) < 16) { + opcode = reference ? Dops.MOVE_OBJECT : (category1 ? Dops.MOVE : Dops.MOVE_WIDE); + } else if (destReg < 256) { + opcode = reference ? Dops.MOVE_OBJECT_FROM16 + : (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16); + } else { + opcode = reference ? Dops.MOVE_OBJECT_16 : (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16); } - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * <p><b>Note:</b> In the unlikely event that an instruction takes - * absolutely no registers (e.g., a {@code nop} or a - * no-argument no-result static method call), then the given - * register list may be passed as {@link - * RegisterSpecList#EMPTY}.</p> - * - * @param opcode the opcode; one of the constants from {@link Dops} - * @param position {@code non-null;} source position - * @param registers {@code non-null;} register list, including a - * result register if appropriate (that is, registers may be either - * ins and outs) - */ - public DalvInsn(Dop opcode, SourcePosition position, - RegisterSpecList registers) { - if (opcode == null) { - throw new NullPointerException("opcode == null"); - } - - if (position == null) { - throw new NullPointerException("position == null"); - } - - if (registers == null) { - throw new NullPointerException("registers == null"); - } - - this.address = -1; - this.opcode = opcode; - this.position = position; - this.registers = registers; + return new SimpleInsn(opcode, position, RegisterSpecList.make(dest, src)); + } + + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * <p><b>Note:</b> In the unlikely event that an instruction takes + * absolutely no registers (e.g., a {@code nop} or a + * no-argument no-result static method call), then the given + * register list may be passed as {@link + * RegisterSpecList#EMPTY}.</p> + * + * @param opcode the opcode; one of the constants from {@link Dops} + * @param position {@code non-null;} source position + * @param registers {@code non-null;} register list, including a + * result register if appropriate (that is, registers may be either + * ins and outs) + */ + public DalvInsn(Dop opcode, SourcePosition position, RegisterSpecList registers) { + if (opcode == null) { + throw new NullPointerException("opcode == null"); } - /** {@inheritDoc} */ - @Override - public final String toString() { - StringBuffer sb = new StringBuffer(100); - - sb.append(identifierString()); - sb.append(' '); - sb.append(position); - - sb.append(": "); - sb.append(opcode.getName()); - - boolean needComma = false; - if (registers.size() != 0) { - sb.append(registers.toHuman(" ", ", ", null)); - needComma = true; - } - - String extra = argString(); - if (extra != null) { - if (needComma) { - sb.append(','); - } - sb.append(' '); - sb.append(extra); - } - - return sb.toString(); + if (position == null) { + throw new NullPointerException("position == null"); } - /** - * Gets whether the address of this instruction is known. - * - * @see #getAddress - * @see #setAddress - */ - public final boolean hasAddress() { - return (address >= 0); + if (registers == null) { + throw new NullPointerException("registers == null"); } - /** - * Gets the output address of this instruction, if it is known. This throws - * a {@code RuntimeException} if it has not yet been set. - * - * @see #setAddress - * - * @return {@code >= 0;} the output address - */ - public final int getAddress() { - if (address < 0) { - throw new RuntimeException("address not yet known"); - } - - return address; - } + this.address = -1; + this.opcode = opcode; + this.position = position; + this.registers = registers; + } - /** - * Gets the opcode. - * - * @return {@code non-null;} the opcode - */ - public final Dop getOpcode() { - return opcode; - } + /** {@inheritDoc} */ + @Override + public final String toString() { + StringBuffer sb = new StringBuffer(100); - /** - * Gets the source position. - * - * @return {@code non-null;} the source position - */ - public final SourcePosition getPosition() { - return position; - } + sb.append(identifierString()); + sb.append(' '); + sb.append(position); - /** - * Gets the register list for this instruction. - * - * @return {@code non-null;} the registers - */ - public final RegisterSpecList getRegisters() { - return registers; - } + sb.append(": "); + sb.append(opcode.getName()); - /** - * Returns whether this instance's opcode uses a result register. - * This method is a convenient shorthand for - * {@code getOpcode().hasResult()}. - * - * @return {@code true} iff this opcode uses a result register - */ - public final boolean hasResult() { - return opcode.hasResult(); + boolean needComma = false; + if (registers.size() != 0) { + sb.append(registers.toHuman(" ", ", ", null)); + needComma = true; } - /** - * Gets the minimum distinct registers required for this instruction. - * Uses the given BitSet to determine which registers require - * replacement, and ignores registers that are already compatible. - * This assumes that the result (if any) can share registers with the - * sources (if any), that each source register is unique, and that - * (to be explicit here) category-2 values take up two consecutive - * registers. - * - * @param compatRegs {@code non-null;} set of compatible registers - * @return {@code >= 0;} the minimum distinct register requirement - */ - public final int getMinimumRegisterRequirement(BitSet compatRegs) { - boolean hasResult = hasResult(); - int regSz = registers.size(); - int resultRequirement = 0; - int sourceRequirement = 0; - - if (hasResult && !compatRegs.get(0)) { - resultRequirement = registers.get(0).getCategory(); - } - - for (int i = hasResult ? 1 : 0; i < regSz; i++) { - if (!compatRegs.get(i)) { - sourceRequirement += registers.get(i).getCategory(); - } - } - - return Math.max(sourceRequirement, resultRequirement); + String extra = argString(); + if (extra != null) { + if (needComma) { + sb.append(','); + } + sb.append(' '); + sb.append(extra); } - /** - * Gets the instruction that is equivalent to this one, except that - * it uses sequential registers starting at {@code 0} (storing - * the result, if any, in register {@code 0} as well). - * - * @return {@code non-null;} the replacement - */ - public DalvInsn getLowRegVersion() { - RegisterSpecList regs = - registers.withExpandedRegisters(0, hasResult(), null); - return withRegisters(regs); + return sb.toString(); + } + + /** + * Gets whether the address of this instruction is known. + * + * @see #getAddress + * @see #setAddress + */ + public final boolean hasAddress() { + return (address >= 0); + } + + /** + * Gets the output address of this instruction, if it is known. This throws + * a {@code RuntimeException} if it has not yet been set. + * + * @see #setAddress + * + * @return {@code >= 0;} the output address + */ + public final int getAddress() { + if (address < 0) { + throw new RuntimeException("address not yet known"); } - /** - * Gets the instruction prefix required, if any, to use in an expanded - * version of this instance. Will not generate moves for registers - * marked compatible to the format by the given BitSet. - * - * @see #expandedVersion - * - * @param compatRegs {@code non-null;} set of compatible registers - * @return {@code null-ok;} the prefix, if any - */ - public DalvInsn expandedPrefix(BitSet compatRegs) { - RegisterSpecList regs = registers; - boolean firstBit = compatRegs.get(0); - - if (hasResult()) compatRegs.set(0); - - regs = regs.subset(compatRegs); - - if (hasResult()) compatRegs.set(0, firstBit); - - if (regs.size() == 0) return null; + return address; + } + + /** + * Gets the opcode. + * + * @return {@code non-null;} the opcode + */ + public final Dop getOpcode() { + return opcode; + } + + /** + * Gets the source position. + * + * @return {@code non-null;} the source position + */ + public final SourcePosition getPosition() { + return position; + } + + /** + * Gets the register list for this instruction. + * + * @return {@code non-null;} the registers + */ + public final RegisterSpecList getRegisters() { + return registers; + } + + /** + * Returns whether this instance's opcode uses a result register. + * This method is a convenient shorthand for + * {@code getOpcode().hasResult()}. + * + * @return {@code true} iff this opcode uses a result register + */ + public final boolean hasResult() { + return opcode.hasResult(); + } + + /** + * Gets the minimum distinct registers required for this instruction. + * Uses the given BitSet to determine which registers require + * replacement, and ignores registers that are already compatible. + * This assumes that the result (if any) can share registers with the + * sources (if any), that each source register is unique, and that + * (to be explicit here) category-2 values take up two consecutive + * registers. + * + * @param compatRegs {@code non-null;} set of compatible registers + * @return {@code >= 0;} the minimum distinct register requirement + */ + public final int getMinimumRegisterRequirement(BitSet compatRegs) { + boolean hasResult = hasResult(); + int regSz = registers.size(); + int resultRequirement = 0; + int sourceRequirement = 0; + + if (hasResult && !compatRegs.get(0)) { + resultRequirement = registers.get(0).getCategory(); + } - return new HighRegisterPrefix(position, regs); + for (int i = hasResult ? 1 : 0; i < regSz; i++) { + if (!compatRegs.get(i)) { + sourceRequirement += registers.get(i).getCategory(); + } } - /** - * Gets the instruction suffix required, if any, to use in an expanded - * version of this instance. Will not generate a move for a register - * marked compatible to the format by the given BitSet. - * - * @see #expandedVersion - * - * @param compatRegs {@code non-null;} set of compatible registers - * @return {@code null-ok;} the suffix, if any - */ - public DalvInsn expandedSuffix(BitSet compatRegs) { - if (hasResult() && !compatRegs.get(0)) { - RegisterSpec r = registers.get(0); - return makeMove(position, r, r.withReg(0)); - } else { - return null; - } + return Math.max(sourceRequirement, resultRequirement); + } + + /** + * Gets the instruction that is equivalent to this one, except that + * it uses sequential registers starting at {@code 0} (storing + * the result, if any, in register {@code 0} as well). + * + * @return {@code non-null;} the replacement + */ + public DalvInsn getLowRegVersion() { + RegisterSpecList regs = registers.withExpandedRegisters(0, hasResult(), null); + return withRegisters(regs); + } + + /** + * Gets the instruction prefix required, if any, to use in an expanded + * version of this instance. Will not generate moves for registers + * marked compatible to the format by the given BitSet. + * + * @see #expandedVersion + * + * @param compatRegs {@code non-null;} set of compatible registers + * @return {@code null-ok;} the prefix, if any + */ + public DalvInsn expandedPrefix(BitSet compatRegs) { + RegisterSpecList regs = registers; + boolean firstBit = compatRegs.get(0); + + if (hasResult()) { + compatRegs.set(0); } - /** - * Gets the instruction that is equivalent to this one, except that - * it replaces incompatible registers with sequential registers - * starting at {@code 0} (storing the result, if any, in register - * {@code 0} as well). The sequence of instructions from - * {@link #expandedPrefix} and {@link #expandedSuffix} (if non-null) - * surrounding the result of a call to this method are the expanded - * transformation of this instance, and it is guaranteed that the - * number of low registers used will be the number returned by - * {@link #getMinimumRegisterRequirement}. - * - * @param compatRegs {@code non-null;} set of compatible registers - * @return {@code non-null;} the replacement - */ - public DalvInsn expandedVersion(BitSet compatRegs) { - RegisterSpecList regs = - registers.withExpandedRegisters(0, hasResult(), compatRegs); - return withRegisters(regs); + regs = regs.subset(compatRegs); + + if (hasResult()) { + compatRegs.set(0, firstBit); } - /** - * Gets the short identifier for this instruction. This is its - * address, if assigned, or its identity hashcode if not. - * - * @return {@code non-null;} the identifier - */ - public final String identifierString() { - if (address != -1) { - return String.format("%04x", address); - } - - return Hex.u4(System.identityHashCode(this)); + if (regs.size() == 0) { + return null; } - /** - * Returns the string form of this instance suitable for inclusion in - * a human-oriented listing dump. This method will return {@code null} - * if this instance should not appear in a listing. - * - * @param prefix {@code non-null;} prefix before the address; each follow-on - * line will be indented to match as well - * @param width {@code >= 0;} the width of the output or {@code 0} for - * unlimited width - * @param noteIndices whether to include an explicit notation of - * constant pool indices - * @return {@code null-ok;} the string form or {@code null} if this - * instance should not appear in a listing - */ - public final String listingString(String prefix, int width, - boolean noteIndices) { - String insnPerSe = listingString0(noteIndices); - - if (insnPerSe == null) { - return null; - } - - String addr = prefix + identifierString() + ": "; - int w1 = addr.length(); - int w2 = (width == 0) ? insnPerSe.length() : (width - w1); - - return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2); + return new HighRegisterPrefix(position, regs); + } + + /** + * Gets the instruction suffix required, if any, to use in an expanded + * version of this instance. Will not generate a move for a register + * marked compatible to the format by the given BitSet. + * + * @see #expandedVersion + * + * @param compatRegs {@code non-null;} set of compatible registers + * @return {@code null-ok;} the suffix, if any + */ + public DalvInsn expandedSuffix(BitSet compatRegs) { + if (hasResult() && !compatRegs.get(0)) { + RegisterSpec r = registers.get(0); + return makeMove(position, r, r.withReg(0)); + } else { + return null; + } + } + + /** + * Gets the instruction that is equivalent to this one, except that + * it replaces incompatible registers with sequential registers + * starting at {@code 0} (storing the result, if any, in register + * {@code 0} as well). The sequence of instructions from + * {@link #expandedPrefix} and {@link #expandedSuffix} (if non-null) + * surrounding the result of a call to this method are the expanded + * transformation of this instance, and it is guaranteed that the + * number of low registers used will be the number returned by + * {@link #getMinimumRegisterRequirement}. + * + * @param compatRegs {@code non-null;} set of compatible registers + * @return {@code non-null;} the replacement + */ + public DalvInsn expandedVersion(BitSet compatRegs) { + RegisterSpecList regs = registers.withExpandedRegisters(0, hasResult(), compatRegs); + return withRegisters(regs); + } + + /** + * Gets the short identifier for this instruction. This is its + * address, if assigned, or its identity hashcode if not. + * + * @return {@code non-null;} the identifier + */ + public final String identifierString() { + if (address != -1) { + return String.format("%04x", address); } - /** - * Sets the output address. - * - * @param address {@code >= 0;} the output address - */ - public final void setAddress(int address) { - if (address < 0) { - throw new IllegalArgumentException("address < 0"); - } - - this.address = address; + return Hex.u4(System.identityHashCode(this)); + } + + /** + * Returns the string form of this instance suitable for inclusion in + * a human-oriented listing dump. This method will return {@code null} + * if this instance should not appear in a listing. + * + * @param prefix {@code non-null;} prefix before the address; each follow-on + * line will be indented to match as well + * @param width {@code >= 0;} the width of the output or {@code 0} for + * unlimited width + * @param noteIndices whether to include an explicit notation of + * constant pool indices + * @return {@code null-ok;} the string form or {@code null} if this + * instance should not appear in a listing + */ + public final String listingString(String prefix, int width, boolean noteIndices) { + String insnPerSe = listingString0(noteIndices); + + if (insnPerSe == null) { + return null; } - /** - * Gets the address immediately after this instance. This is only - * calculable if this instance's address is known, and it is equal - * to the address plus the length of the instruction format of this - * instance's opcode. - * - * @return {@code >= 0;} the next address - */ - public final int getNextAddress() { - return getAddress() + codeSize(); + String addr = prefix + identifierString() + ": "; + int w1 = addr.length(); + int w2 = (width == 0) ? insnPerSe.length() : (width - w1); + + return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2); + } + + /** + * Sets the output address. + * + * @param address {@code >= 0;} the output address + */ + public final void setAddress(int address) { + if (address < 0) { + throw new IllegalArgumentException("address < 0"); } - /** - * Gets the size of this instruction, in 16-bit code units. - * - * @return {@code >= 0;} the code size of this instruction - */ - public abstract int codeSize(); - - /** - * Writes this instance to the given output. This method should - * never annotate the output. - * - * @param out {@code non-null;} where to write to - */ - public abstract void writeTo(AnnotatedOutput out); - - /** - * Returns an instance that is just like this one, except that its - * opcode is replaced by the one given, and its address is reset. - * - * @param opcode {@code non-null;} the new opcode - * @return {@code non-null;} an appropriately-constructed instance - */ - public abstract DalvInsn withOpcode(Dop opcode); - - /** - * Returns an instance that is just like this one, except that all - * register references have been offset by the given delta, and its - * address is reset. - * - * @param delta the amount to offset register references by - * @return {@code non-null;} an appropriately-constructed instance - */ - public abstract DalvInsn withRegisterOffset(int delta); - - /** - * Returns an instance that is just like this one, except that the - * register list is replaced by the given one, and its address is - * reset. - * - * @param registers {@code non-null;} new register list - * @return {@code non-null;} an appropriately-constructed instance - */ - public abstract DalvInsn withRegisters(RegisterSpecList registers); - - /** - * Gets the string form for any arguments to this instance. Subclasses - * must override this. - * - * @return {@code null-ok;} the string version of any arguments or - * {@code null} if there are none - */ - protected abstract String argString(); - - /** - * Helper for {@link #listingString}, which returns the string - * form of this instance suitable for inclusion in a - * human-oriented listing dump, not including the instruction - * address and without respect for any output formatting. This - * method should return {@code null} if this instance should - * not appear in a listing. - * - * @param noteIndices whether to include an explicit notation of - * constant pool indices - * @return {@code null-ok;} the listing string - */ - protected abstract String listingString0(boolean noteIndices); + this.address = address; + } + + /** + * Gets the address immediately after this instance. This is only + * calculable if this instance's address is known, and it is equal + * to the address plus the length of the instruction format of this + * instance's opcode. + * + * @return {@code >= 0;} the next address + */ + public final int getNextAddress() { + return getAddress() + codeSize(); + } + + /** + * Gets the size of this instruction, in 16-bit code units. + * + * @return {@code >= 0;} the code size of this instruction + */ + public abstract int codeSize(); + + /** + * Writes this instance to the given output. This method should + * never annotate the output. + * + * @param out {@code non-null;} where to write to + */ + public abstract void writeTo(AnnotatedOutput out); + + /** + * Returns an instance that is just like this one, except that its + * opcode is replaced by the one given, and its address is reset. + * + * @param opcode {@code non-null;} the new opcode + * @return {@code non-null;} an appropriately-constructed instance + */ + public abstract DalvInsn withOpcode(Dop opcode); + + /** + * Returns an instance that is just like this one, except that all + * register references have been offset by the given delta, and its + * address is reset. + * + * @param delta the amount to offset register references by + * @return {@code non-null;} an appropriately-constructed instance + */ + public abstract DalvInsn withRegisterOffset(int delta); + + /** + * Returns an instance that is just like this one, except that the + * register list is replaced by the given one, and its address is + * reset. + * + * @param registers {@code non-null;} new register list + * @return {@code non-null;} an appropriately-constructed instance + */ + public abstract DalvInsn withRegisters(RegisterSpecList registers); + + /** + * Gets the string form for any arguments to this instance. Subclasses + * must override this. + * + * @return {@code null-ok;} the string version of any arguments or + * {@code null} if there are none + */ + protected abstract String argString(); + + /** + * Helper for {@link #listingString}, which returns the string + * form of this instance suitable for inclusion in a + * human-oriented listing dump, not including the instruction + * address and without respect for any output formatting. This + * method should return {@code null} if this instance should + * not appear in a listing. + * + * @param noteIndices whether to include an explicit notation of + * constant pool indices + * @return {@code null-ok;} the listing string + */ + protected abstract String listingString0(boolean noteIndices); } diff --git a/dx/src/com/android/jack/dx/dex/code/DalvInsnList.java b/dx/src/com/android/jack/dx/dex/code/DalvInsnList.java index 80fe920..1d453d9 100644 --- a/dx/src/com/android/jack/dx/dex/code/DalvInsnList.java +++ b/dx/src/com/android/jack/dx/dex/code/DalvInsnList.java @@ -35,235 +35,231 @@ import java.util.ArrayList; */ public final class DalvInsnList extends FixedSizeList { - /** - * The amount of register space, in register units, required for this - * code block. This may be greater than the largest observed register+ - * category because the method this code block exists in may - * specify arguments that are unused by the method. - */ - private final int regCount; - - /** - * Constructs and returns an immutable instance whose elements are - * identical to the ones in the given list, in the same order. - * - * @param list {@code non-null;} the list to use for elements - * @param regCount count, in register-units, of the number of registers - * this code block requires. - * @return {@code non-null;} an appropriately-constructed instance of this - * class - */ - public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list, - int regCount) { - int size = list.size(); - DalvInsnList result = new DalvInsnList(size, regCount); - - for (int i = 0; i < size; i++) { - result.set(i, list.get(i)); - } - - result.setImmutable(); - return result; + /** + * The amount of register space, in register units, required for this + * code block. This may be greater than the largest observed register+ + * category because the method this code block exists in may + * specify arguments that are unused by the method. + */ + private final int regCount; + + /** + * Constructs and returns an immutable instance whose elements are + * identical to the ones in the given list, in the same order. + * + * @param list {@code non-null;} the list to use for elements + * @param regCount count, in register-units, of the number of registers + * this code block requires. + * @return {@code non-null;} an appropriately-constructed instance of this + * class + */ + public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list, int regCount) { + int size = list.size(); + DalvInsnList result = new DalvInsnList(size, regCount); + + for (int i = 0; i < size; i++) { + result.set(i, list.get(i)); } - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size the size of the list - */ - public DalvInsnList(int size, int regCount) { - super(size); - this.regCount = regCount; - } - - /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index - */ - public DalvInsn get(int n) { - return (DalvInsn) get0(n); - } - - /** - * Sets the instruction at the given index. - * - * @param n {@code >= 0, < size();} which index - * @param insn {@code non-null;} the instruction to set at {@code n} - */ - public void set(int n, DalvInsn insn) { - set0(n, insn); - } - - /** - * Gets the size of this instance, in 16-bit code units. This will only - * return a meaningful result if the instructions in this instance all - * have valid addresses. - * - * @return {@code >= 0;} the size - */ - public int codeSize() { - int sz = size(); - - if (sz == 0) { - return 0; - } - - DalvInsn last = get(sz - 1); - return last.getNextAddress(); + result.setImmutable(); + return result; + } + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size the size of the list + */ + public DalvInsnList(int size, int regCount) { + super(size); + this.regCount = regCount; + } + + /** + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index + */ + public DalvInsn get(int n) { + return (DalvInsn) get0(n); + } + + /** + * Sets the instruction at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param insn {@code non-null;} the instruction to set at {@code n} + */ + public void set(int n, DalvInsn insn) { + set0(n, insn); + } + + /** + * Gets the size of this instance, in 16-bit code units. This will only + * return a meaningful result if the instructions in this instance all + * have valid addresses. + * + * @return {@code >= 0;} the size + */ + public int codeSize() { + int sz = size(); + + if (sz == 0) { + return 0; } - /** - * Writes all the instructions in this instance to the given output - * destination. - * - * @param out {@code non-null;} where to write to - */ - public void writeTo(AnnotatedOutput out) { - int startCursor = out.getCursor(); - int sz = size(); - - if (out.annotates()) { - boolean verbose = out.isVerbose(); - - for (int i = 0; i < sz; i++) { - DalvInsn insn = (DalvInsn) get0(i); - int codeBytes = insn.codeSize() * 2; - String s; - - if ((codeBytes != 0) || verbose) { - s = insn.listingString(" ", out.getAnnotationWidth(), - true); - } else { - s = null; - } - - if (s != null) { - out.annotate(codeBytes, s); - } else if (codeBytes != 0) { - out.annotate(codeBytes, ""); - } - } - } - - for (int i = 0; i < sz; i++) { - DalvInsn insn = (DalvInsn) get0(i); - try { - insn.writeTo(out); - } catch (RuntimeException ex) { - throw ExceptionWithContext.withContext(ex, - "...while writing " + insn); - } + DalvInsn last = get(sz - 1); + return last.getNextAddress(); + } + + /** + * Writes all the instructions in this instance to the given output + * destination. + * + * @param out {@code non-null;} where to write to + */ + public void writeTo(AnnotatedOutput out) { + int startCursor = out.getCursor(); + int sz = size(); + + if (out.annotates()) { + boolean verbose = out.isVerbose(); + + for (int i = 0; i < sz; i++) { + DalvInsn insn = (DalvInsn) get0(i); + int codeBytes = insn.codeSize() * 2; + String s; + + if ((codeBytes != 0) || verbose) { + s = insn.listingString(" ", out.getAnnotationWidth(), true); + } else { + s = null; } - // Sanity check of the amount written. - int written = (out.getCursor() - startCursor) / 2; - if (written != codeSize()) { - throw new RuntimeException("write length mismatch; expected " + - codeSize() + " but actually wrote " + written); + if (s != null) { + out.annotate(codeBytes, s); + } else if (codeBytes != 0) { + out.annotate(codeBytes, ""); } + } } - /** - * Gets the minimum required register count implied by this - * instance. This includes any unused parameters that could - * potentially be at the top of the register space. - * @return {@code >= 0;} the required registers size - */ - public int getRegistersSize() { - return regCount; + for (int i = 0; i < sz; i++) { + DalvInsn insn = (DalvInsn) get0(i); + try { + insn.writeTo(out); + } catch (RuntimeException ex) { + throw ExceptionWithContext.withContext(ex, "...while writing " + insn); + } } - /** - * Gets the size of the outgoing arguments area required by this - * method. This is equal to the largest argument word count of any - * method referred to by this instance. - * - * @return {@code >= 0;} the required outgoing arguments size - */ - public int getOutsSize() { - int sz = size(); - int result = 0; - - for (int i = 0; i < sz; i++) { - DalvInsn insn = (DalvInsn) get0(i); - - if (!(insn instanceof CstInsn)) { - continue; - } - - Constant cst = ((CstInsn) insn).getConstant(); - - if (!(cst instanceof CstBaseMethodRef)) { - continue; - } - - boolean isStatic = - (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC); - int count = - ((CstBaseMethodRef) cst).getParameterWordCount(isStatic); - - if (count > result) { - result = count; - } - } - - return result; + // Sanity check of the amount written. + int written = (out.getCursor() - startCursor) / 2; + if (written != codeSize()) { + throw new RuntimeException( + "write length mismatch; expected " + codeSize() + " but actually wrote " + written); + } + } + + /** + * Gets the minimum required register count implied by this + * instance. This includes any unused parameters that could + * potentially be at the top of the register space. + * @return {@code >= 0;} the required registers size + */ + public int getRegistersSize() { + return regCount; + } + + /** + * Gets the size of the outgoing arguments area required by this + * method. This is equal to the largest argument word count of any + * method referred to by this instance. + * + * @return {@code >= 0;} the required outgoing arguments size + */ + public int getOutsSize() { + int sz = size(); + int result = 0; + + for (int i = 0; i < sz; i++) { + DalvInsn insn = (DalvInsn) get0(i); + + if (!(insn instanceof CstInsn)) { + continue; + } + + Constant cst = ((CstInsn) insn).getConstant(); + + if (!(cst instanceof CstBaseMethodRef)) { + continue; + } + + boolean isStatic = (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC); + int count = ((CstBaseMethodRef) cst).getParameterWordCount(isStatic); + + if (count > result) { + result = count; + } } - /** - * Does a human-friendly dump of this instance. - * - * @param out {@code non-null;} where to dump - * @param prefix {@code non-null;} prefix to attach to each line of output - * @param verbose whether to be verbose; verbose output includes - * lines for zero-size instructions and explicit constant pool indices - */ - public void debugPrint(Writer out, String prefix, boolean verbose) { - IndentingWriter iw = new IndentingWriter(out, 0, prefix); - int sz = size(); - - try { - for (int i = 0; i < sz; i++) { - DalvInsn insn = (DalvInsn) get0(i); - String s; - - if ((insn.codeSize() != 0) || verbose) { - s = insn.listingString("", 0, verbose); - } else { - s = null; - } - - if (s != null) { - iw.write(s); - } - } - - iw.flush(); - } catch (IOException ex) { - throw new RuntimeException(ex); + return result; + } + + /** + * Does a human-friendly dump of this instance. + * + * @param out {@code non-null;} where to dump + * @param prefix {@code non-null;} prefix to attach to each line of output + * @param verbose whether to be verbose; verbose output includes + * lines for zero-size instructions and explicit constant pool indices + */ + public void debugPrint(Writer out, String prefix, boolean verbose) { + @SuppressWarnings("resource") + IndentingWriter iw = new IndentingWriter(out, 0, prefix); + int sz = size(); + + try { + for (int i = 0; i < sz; i++) { + DalvInsn insn = (DalvInsn) get0(i); + String s; + + if ((insn.codeSize() != 0) || verbose) { + s = insn.listingString("", 0, verbose); + } else { + s = null; } - } - /** - * Does a human-friendly dump of this instance. - * - * @param out {@code non-null;} where to dump - * @param prefix {@code non-null;} prefix to attach to each line of output - * @param verbose whether to be verbose; verbose output includes - * lines for zero-size instructions - */ - public void debugPrint(OutputStream out, String prefix, boolean verbose) { - Writer w = new OutputStreamWriter(out); - debugPrint(w, prefix, verbose); - - try { - w.flush(); - } catch (IOException ex) { - throw new RuntimeException(ex); + if (s != null) { + iw.write(s); } + } + + iw.flush(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Does a human-friendly dump of this instance. + * + * @param out {@code non-null;} where to dump + * @param prefix {@code non-null;} prefix to attach to each line of output + * @param verbose whether to be verbose; verbose output includes + * lines for zero-size instructions + */ + public void debugPrint(OutputStream out, String prefix, boolean verbose) { + Writer w = new OutputStreamWriter(out); + debugPrint(w, prefix, verbose); + + try { + w.flush(); + } catch (IOException ex) { + throw new RuntimeException(ex); } + } } diff --git a/dx/src/com/android/jack/dx/dex/code/Dop.java b/dx/src/com/android/jack/dx/dex/code/Dop.java index 5b390aa..0a0cbc2 100644 --- a/dx/src/com/android/jack/dx/dex/code/Dop.java +++ b/dx/src/com/android/jack/dx/dex/code/Dop.java @@ -23,151 +23,162 @@ import com.android.jack.dx.io.Opcodes; * Representation of an opcode. */ public final class Dop { - /** {@code Opcodes.isValid();} the opcode value itself */ - private final int opcode; - - /** {@code Opcodes.isValid();} the opcode family */ - private final int family; - - /** - * {@code Opcodes.isValid();} what opcode (by number) to try next - * when attempting to match an opcode to particular arguments; - * {@code Opcodes.NO_NEXT} to indicate that this is the last - * opcode to try in a particular chain - */ - private final int nextOpcode; - - /** {@code non-null;} the instruction format */ - private final InsnFormat format; - - /** whether this opcode uses a result register */ - private final boolean hasResult; - - /** - * Constructs an instance. - * - * @param opcode {@code Opcodes.isValid();} the opcode value - * itself - * @param family {@code Opcodes.isValid();} the opcode family - * @param nextOpcode {@code Opcodes.isValid();} what opcode (by - * number) to try next when attempting to match an opcode to - * particular arguments; {@code Opcodes.NO_NEXT} to indicate that - * this is the last opcode to try in a particular chain - * @param format {@code non-null;} the instruction format - * @param hasResult whether the opcode has a result register; if so it - * is always the first register - */ - public Dop(int opcode, int family, int nextOpcode, InsnFormat format, - boolean hasResult) { - if (!Opcodes.isValidShape(opcode)) { - throw new IllegalArgumentException("bogus opcode"); - } - - if (!Opcodes.isValidShape(family)) { - throw new IllegalArgumentException("bogus family"); - } - - if (!Opcodes.isValidShape(nextOpcode)) { - throw new IllegalArgumentException("bogus nextOpcode"); - } - - if (format == null) { - throw new NullPointerException("format == null"); - } - - this.opcode = opcode; - this.family = family; - this.nextOpcode = nextOpcode; - this.format = format; - this.hasResult = hasResult; + /** {@code Opcodes.isValid();} the opcode value itself */ + private final int opcode; + + /** {@code Opcodes.isValid();} the opcode family */ + private final int family; + + /** + * {@code Opcodes.isValid();} what opcode (by number) to try next + * when attempting to match an opcode to particular arguments; + * {@code Opcodes.NO_NEXT} to indicate that this is the last + * opcode to try in a particular chain + */ + private final int nextOpcode; + + /** {@code non-null;} the instruction format */ + private final InsnFormat format; + + /** whether this opcode uses a result register */ + private final boolean hasResult; + + /** + * Constructs an instance. + * + * @param opcode {@code Opcodes.isValid();} the opcode value + * itself + * @param family {@code Opcodes.isValid();} the opcode family + * @param nextOpcode {@code Opcodes.isValid();} what opcode (by + * number) to try next when attempting to match an opcode to + * particular arguments; {@code Opcodes.NO_NEXT} to indicate that + * this is the last opcode to try in a particular chain + * @param format {@code non-null;} the instruction format + * @param hasResult whether the opcode has a result register; if so it + * is always the first register + */ + public Dop(int opcode, int family, int nextOpcode, InsnFormat format, boolean hasResult) { + if (!Opcodes.isValidShape(opcode)) { + throw new IllegalArgumentException("bogus opcode"); } - /** {@inheritDoc} */ - @Override - public String toString() { - return getName(); + if (!Opcodes.isValidShape(family)) { + throw new IllegalArgumentException("bogus family"); } - /** - * Gets the opcode value. - * - * @return {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the opcode value - */ - public int getOpcode() { - return opcode; + if (!Opcodes.isValidShape(nextOpcode)) { + throw new IllegalArgumentException("bogus nextOpcode"); } - /** - * Gets the opcode family. The opcode family is the unmarked (no - * "/...") opcode that has equivalent semantics to this one. - * - * @return {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the opcode family - */ - public int getFamily() { - return family; + if (format == null) { + throw new NullPointerException("format == null"); } - /** - * Gets the instruction format. - * - * @return {@code non-null;} the instruction format - */ - public InsnFormat getFormat() { - return format; + this.opcode = opcode; + this.family = family; + this.nextOpcode = nextOpcode; + this.format = format; + this.hasResult = hasResult; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return getName(); + } + + /** + * Gets the opcode value. + * + * @return {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the opcode value + */ + public int getOpcode() { + return opcode; + } + + /** + * Gets the opcode family. The opcode family is the unmarked (no + * "/...") opcode that has equivalent semantics to this one. + * + * @return {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the opcode family + */ + public int getFamily() { + return family; + } + + /** + * Gets the instruction format. + * + * @return {@code non-null;} the instruction format + */ + public InsnFormat getFormat() { + return format; + } + + /** + * Returns whether this opcode uses a result register. + * + * @return {@code true} iff this opcode uses a result register + */ + public boolean hasResult() { + return hasResult; + } + + /** + * Gets the opcode name. + * + * @return {@code non-null;} the opcode name + */ + public String getName() { + return OpcodeInfo.getName(opcode); + } + + /** + * Gets the opcode value to try next when attempting to match an + * opcode to particular arguments. This returns {@code + * Opcodes.NO_NEXT} to indicate that this is the last opcode to + * try in a particular chain. + * + * @return {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the opcode value + */ + public int getNextOpcode() { + return nextOpcode; + } + + /** + * Gets the opcode for the opposite test of this instance. This is only + * valid for opcodes which are in fact tests. + * + * @return {@code non-null;} the opposite test + */ + public Dop getOppositeTest() { + switch (opcode) { + case Opcodes.IF_EQ: + return Dops.IF_NE; + case Opcodes.IF_NE: + return Dops.IF_EQ; + case Opcodes.IF_LT: + return Dops.IF_GE; + case Opcodes.IF_GE: + return Dops.IF_LT; + case Opcodes.IF_GT: + return Dops.IF_LE; + case Opcodes.IF_LE: + return Dops.IF_GT; + case Opcodes.IF_EQZ: + return Dops.IF_NEZ; + case Opcodes.IF_NEZ: + return Dops.IF_EQZ; + case Opcodes.IF_LTZ: + return Dops.IF_GEZ; + case Opcodes.IF_GEZ: + return Dops.IF_LTZ; + case Opcodes.IF_GTZ: + return Dops.IF_LEZ; + case Opcodes.IF_LEZ: + return Dops.IF_GTZ; } - /** - * Returns whether this opcode uses a result register. - * - * @return {@code true} iff this opcode uses a result register - */ - public boolean hasResult() { - return hasResult; - } - - /** - * Gets the opcode name. - * - * @return {@code non-null;} the opcode name - */ - public String getName() { - return OpcodeInfo.getName(opcode); - } - - /** - * Gets the opcode value to try next when attempting to match an - * opcode to particular arguments. This returns {@code - * Opcodes.NO_NEXT} to indicate that this is the last opcode to - * try in a particular chain. - * - * @return {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the opcode value - */ - public int getNextOpcode() { - return nextOpcode; - } - - /** - * Gets the opcode for the opposite test of this instance. This is only - * valid for opcodes which are in fact tests. - * - * @return {@code non-null;} the opposite test - */ - public Dop getOppositeTest() { - switch (opcode) { - case Opcodes.IF_EQ: return Dops.IF_NE; - case Opcodes.IF_NE: return Dops.IF_EQ; - case Opcodes.IF_LT: return Dops.IF_GE; - case Opcodes.IF_GE: return Dops.IF_LT; - case Opcodes.IF_GT: return Dops.IF_LE; - case Opcodes.IF_LE: return Dops.IF_GT; - case Opcodes.IF_EQZ: return Dops.IF_NEZ; - case Opcodes.IF_NEZ: return Dops.IF_EQZ; - case Opcodes.IF_LTZ: return Dops.IF_GEZ; - case Opcodes.IF_GEZ: return Dops.IF_LTZ; - case Opcodes.IF_GTZ: return Dops.IF_LEZ; - case Opcodes.IF_LEZ: return Dops.IF_GTZ; - } - - throw new IllegalArgumentException("bogus opcode: " + this); - } + throw new IllegalArgumentException("bogus opcode: " + this); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/Dops.java b/dx/src/com/android/jack/dx/dex/code/Dops.java index 39ea0d3..acaff76 100644 --- a/dx/src/com/android/jack/dx/dex/code/Dops.java +++ b/dx/src/com/android/jack/dx/dex/code/Dops.java @@ -49,1181 +49,962 @@ import com.android.jack.dx.io.Opcodes; * them. */ public final class Dops { - /** {@code non-null;} array containing all the standard instances */ - private static final Dop[] DOPS; - - /** - * pseudo-opcode used for nonstandard formatted "instructions" - * (which are mostly not actually instructions, though they do - * appear in instruction lists). TODO: Retire the usage of this - * constant. - */ - public static final Dop SPECIAL_FORMAT = - new Dop(Opcodes.SPECIAL_FORMAT, Opcodes.SPECIAL_FORMAT, - Opcodes.NO_NEXT, SpecialFormat.THE_ONE, false); - - // BEGIN(dops); GENERATED AUTOMATICALLY BY opcode-gen - public static final Dop NOP = - new Dop(Opcodes.NOP, Opcodes.NOP, - Opcodes.NO_NEXT, Form10x.THE_ONE, false); - - public static final Dop MOVE = - new Dop(Opcodes.MOVE, Opcodes.MOVE, - Opcodes.MOVE_FROM16, Form12x.THE_ONE, true); - - public static final Dop MOVE_FROM16 = - new Dop(Opcodes.MOVE_FROM16, Opcodes.MOVE, - Opcodes.MOVE_16, Form22x.THE_ONE, true); - - public static final Dop MOVE_16 = - new Dop(Opcodes.MOVE_16, Opcodes.MOVE, - Opcodes.NO_NEXT, Form32x.THE_ONE, true); - - public static final Dop MOVE_WIDE = - new Dop(Opcodes.MOVE_WIDE, Opcodes.MOVE_WIDE, - Opcodes.MOVE_WIDE_FROM16, Form12x.THE_ONE, true); - - public static final Dop MOVE_WIDE_FROM16 = - new Dop(Opcodes.MOVE_WIDE_FROM16, Opcodes.MOVE_WIDE, - Opcodes.MOVE_WIDE_16, Form22x.THE_ONE, true); - - public static final Dop MOVE_WIDE_16 = - new Dop(Opcodes.MOVE_WIDE_16, Opcodes.MOVE_WIDE, - Opcodes.NO_NEXT, Form32x.THE_ONE, true); - - public static final Dop MOVE_OBJECT = - new Dop(Opcodes.MOVE_OBJECT, Opcodes.MOVE_OBJECT, - Opcodes.MOVE_OBJECT_FROM16, Form12x.THE_ONE, true); - - public static final Dop MOVE_OBJECT_FROM16 = - new Dop(Opcodes.MOVE_OBJECT_FROM16, Opcodes.MOVE_OBJECT, - Opcodes.MOVE_OBJECT_16, Form22x.THE_ONE, true); - - public static final Dop MOVE_OBJECT_16 = - new Dop(Opcodes.MOVE_OBJECT_16, Opcodes.MOVE_OBJECT, - Opcodes.NO_NEXT, Form32x.THE_ONE, true); - - public static final Dop MOVE_RESULT = - new Dop(Opcodes.MOVE_RESULT, Opcodes.MOVE_RESULT, - Opcodes.NO_NEXT, Form11x.THE_ONE, true); - - public static final Dop MOVE_RESULT_WIDE = - new Dop(Opcodes.MOVE_RESULT_WIDE, Opcodes.MOVE_RESULT_WIDE, - Opcodes.NO_NEXT, Form11x.THE_ONE, true); - - public static final Dop MOVE_RESULT_OBJECT = - new Dop(Opcodes.MOVE_RESULT_OBJECT, Opcodes.MOVE_RESULT_OBJECT, - Opcodes.NO_NEXT, Form11x.THE_ONE, true); - - public static final Dop MOVE_EXCEPTION = - new Dop(Opcodes.MOVE_EXCEPTION, Opcodes.MOVE_EXCEPTION, - Opcodes.NO_NEXT, Form11x.THE_ONE, true); - - public static final Dop RETURN_VOID = - new Dop(Opcodes.RETURN_VOID, Opcodes.RETURN_VOID, - Opcodes.NO_NEXT, Form10x.THE_ONE, false); - - public static final Dop RETURN = - new Dop(Opcodes.RETURN, Opcodes.RETURN, - Opcodes.NO_NEXT, Form11x.THE_ONE, false); - - public static final Dop RETURN_WIDE = - new Dop(Opcodes.RETURN_WIDE, Opcodes.RETURN_WIDE, - Opcodes.NO_NEXT, Form11x.THE_ONE, false); - - public static final Dop RETURN_OBJECT = - new Dop(Opcodes.RETURN_OBJECT, Opcodes.RETURN_OBJECT, - Opcodes.NO_NEXT, Form11x.THE_ONE, false); - - public static final Dop CONST_4 = - new Dop(Opcodes.CONST_4, Opcodes.CONST, - Opcodes.CONST_16, Form11n.THE_ONE, true); - - public static final Dop CONST_16 = - new Dop(Opcodes.CONST_16, Opcodes.CONST, - Opcodes.CONST_HIGH16, Form21s.THE_ONE, true); - - public static final Dop CONST = - new Dop(Opcodes.CONST, Opcodes.CONST, - Opcodes.NO_NEXT, Form31i.THE_ONE, true); - - public static final Dop CONST_HIGH16 = - new Dop(Opcodes.CONST_HIGH16, Opcodes.CONST, - Opcodes.CONST, Form21h.THE_ONE, true); - - public static final Dop CONST_WIDE_16 = - new Dop(Opcodes.CONST_WIDE_16, Opcodes.CONST_WIDE, - Opcodes.CONST_WIDE_HIGH16, Form21s.THE_ONE, true); - - public static final Dop CONST_WIDE_32 = - new Dop(Opcodes.CONST_WIDE_32, Opcodes.CONST_WIDE, - Opcodes.CONST_WIDE, Form31i.THE_ONE, true); + /** {@code non-null;} array containing all the standard instances */ + private static final Dop[] DOPS; - public static final Dop CONST_WIDE = - new Dop(Opcodes.CONST_WIDE, Opcodes.CONST_WIDE, - Opcodes.NO_NEXT, Form51l.THE_ONE, true); + /** + * pseudo-opcode used for nonstandard formatted "instructions" + * (which are mostly not actually instructions, though they do + * appear in instruction lists). TODO(dx team): Retire the usage of this + * constant. + */ + public static final Dop SPECIAL_FORMAT = new Dop(Opcodes.SPECIAL_FORMAT, Opcodes.SPECIAL_FORMAT, + Opcodes.NO_NEXT, SpecialFormat.THE_ONE, false); - public static final Dop CONST_WIDE_HIGH16 = - new Dop(Opcodes.CONST_WIDE_HIGH16, Opcodes.CONST_WIDE, - Opcodes.CONST_WIDE_32, Form21h.THE_ONE, true); + // BEGIN(dops); GENERATED AUTOMATICALLY BY opcode-gen + public static final Dop NOP = + new Dop(Opcodes.NOP, Opcodes.NOP, Opcodes.NO_NEXT, Form10x.THE_ONE, false); - public static final Dop CONST_STRING = - new Dop(Opcodes.CONST_STRING, Opcodes.CONST_STRING, - Opcodes.CONST_STRING_JUMBO, Form21c.THE_ONE, true); + public static final Dop MOVE = + new Dop(Opcodes.MOVE, Opcodes.MOVE, Opcodes.MOVE_FROM16, Form12x.THE_ONE, true); - public static final Dop CONST_STRING_JUMBO = - new Dop(Opcodes.CONST_STRING_JUMBO, Opcodes.CONST_STRING, - Opcodes.NO_NEXT, Form31c.THE_ONE, true); + public static final Dop MOVE_FROM16 = + new Dop(Opcodes.MOVE_FROM16, Opcodes.MOVE, Opcodes.MOVE_16, Form22x.THE_ONE, true); - public static final Dop CONST_CLASS = - new Dop(Opcodes.CONST_CLASS, Opcodes.CONST_CLASS, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop MOVE_16 = + new Dop(Opcodes.MOVE_16, Opcodes.MOVE, Opcodes.NO_NEXT, Form32x.THE_ONE, true); - public static final Dop MONITOR_ENTER = - new Dop(Opcodes.MONITOR_ENTER, Opcodes.MONITOR_ENTER, - Opcodes.NO_NEXT, Form11x.THE_ONE, false); + public static final Dop MOVE_WIDE = new Dop(Opcodes.MOVE_WIDE, Opcodes.MOVE_WIDE, + Opcodes.MOVE_WIDE_FROM16, Form12x.THE_ONE, true); - public static final Dop MONITOR_EXIT = - new Dop(Opcodes.MONITOR_EXIT, Opcodes.MONITOR_EXIT, - Opcodes.NO_NEXT, Form11x.THE_ONE, false); + public static final Dop MOVE_WIDE_FROM16 = new Dop(Opcodes.MOVE_WIDE_FROM16, Opcodes.MOVE_WIDE, + Opcodes.MOVE_WIDE_16, Form22x.THE_ONE, true); - public static final Dop CHECK_CAST = - new Dop(Opcodes.CHECK_CAST, Opcodes.CHECK_CAST, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop MOVE_WIDE_16 = + new Dop(Opcodes.MOVE_WIDE_16, Opcodes.MOVE_WIDE, Opcodes.NO_NEXT, Form32x.THE_ONE, true); - public static final Dop INSTANCE_OF = - new Dop(Opcodes.INSTANCE_OF, Opcodes.INSTANCE_OF, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop MOVE_OBJECT = new Dop(Opcodes.MOVE_OBJECT, Opcodes.MOVE_OBJECT, + Opcodes.MOVE_OBJECT_FROM16, Form12x.THE_ONE, true); - public static final Dop ARRAY_LENGTH = - new Dop(Opcodes.ARRAY_LENGTH, Opcodes.ARRAY_LENGTH, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop MOVE_OBJECT_FROM16 = new Dop(Opcodes.MOVE_OBJECT_FROM16, + Opcodes.MOVE_OBJECT, Opcodes.MOVE_OBJECT_16, Form22x.THE_ONE, true); - public static final Dop NEW_INSTANCE = - new Dop(Opcodes.NEW_INSTANCE, Opcodes.NEW_INSTANCE, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop MOVE_OBJECT_16 = + new Dop(Opcodes.MOVE_OBJECT_16, Opcodes.MOVE_OBJECT, Opcodes.NO_NEXT, Form32x.THE_ONE, true); - public static final Dop NEW_ARRAY = - new Dop(Opcodes.NEW_ARRAY, Opcodes.NEW_ARRAY, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop MOVE_RESULT = + new Dop(Opcodes.MOVE_RESULT, Opcodes.MOVE_RESULT, Opcodes.NO_NEXT, Form11x.THE_ONE, true); - public static final Dop FILLED_NEW_ARRAY = - new Dop(Opcodes.FILLED_NEW_ARRAY, Opcodes.FILLED_NEW_ARRAY, - Opcodes.FILLED_NEW_ARRAY_RANGE, Form35c.THE_ONE, false); + public static final Dop MOVE_RESULT_WIDE = new Dop(Opcodes.MOVE_RESULT_WIDE, + Opcodes.MOVE_RESULT_WIDE, Opcodes.NO_NEXT, Form11x.THE_ONE, true); - public static final Dop FILLED_NEW_ARRAY_RANGE = - new Dop(Opcodes.FILLED_NEW_ARRAY_RANGE, Opcodes.FILLED_NEW_ARRAY, - Opcodes.NO_NEXT, Form3rc.THE_ONE, false); + public static final Dop MOVE_RESULT_OBJECT = new Dop(Opcodes.MOVE_RESULT_OBJECT, + Opcodes.MOVE_RESULT_OBJECT, Opcodes.NO_NEXT, Form11x.THE_ONE, true); - public static final Dop FILL_ARRAY_DATA = - new Dop(Opcodes.FILL_ARRAY_DATA, Opcodes.FILL_ARRAY_DATA, - Opcodes.NO_NEXT, Form31t.THE_ONE, false); + public static final Dop MOVE_EXCEPTION = new Dop(Opcodes.MOVE_EXCEPTION, Opcodes.MOVE_EXCEPTION, + Opcodes.NO_NEXT, Form11x.THE_ONE, true); - public static final Dop THROW = - new Dop(Opcodes.THROW, Opcodes.THROW, - Opcodes.NO_NEXT, Form11x.THE_ONE, false); + public static final Dop RETURN_VOID = + new Dop(Opcodes.RETURN_VOID, Opcodes.RETURN_VOID, Opcodes.NO_NEXT, Form10x.THE_ONE, false); - public static final Dop GOTO = - new Dop(Opcodes.GOTO, Opcodes.GOTO, - Opcodes.GOTO_16, Form10t.THE_ONE, false); + public static final Dop RETURN = + new Dop(Opcodes.RETURN, Opcodes.RETURN, Opcodes.NO_NEXT, Form11x.THE_ONE, false); - public static final Dop GOTO_16 = - new Dop(Opcodes.GOTO_16, Opcodes.GOTO, - Opcodes.GOTO_32, Form20t.THE_ONE, false); + public static final Dop RETURN_WIDE = + new Dop(Opcodes.RETURN_WIDE, Opcodes.RETURN_WIDE, Opcodes.NO_NEXT, Form11x.THE_ONE, false); - public static final Dop GOTO_32 = - new Dop(Opcodes.GOTO_32, Opcodes.GOTO, - Opcodes.NO_NEXT, Form30t.THE_ONE, false); + public static final Dop RETURN_OBJECT = new Dop(Opcodes.RETURN_OBJECT, Opcodes.RETURN_OBJECT, + Opcodes.NO_NEXT, Form11x.THE_ONE, false); - public static final Dop PACKED_SWITCH = - new Dop(Opcodes.PACKED_SWITCH, Opcodes.PACKED_SWITCH, - Opcodes.NO_NEXT, Form31t.THE_ONE, false); + public static final Dop CONST_4 = + new Dop(Opcodes.CONST_4, Opcodes.CONST, Opcodes.CONST_16, Form11n.THE_ONE, true); - public static final Dop SPARSE_SWITCH = - new Dop(Opcodes.SPARSE_SWITCH, Opcodes.SPARSE_SWITCH, - Opcodes.NO_NEXT, Form31t.THE_ONE, false); + public static final Dop CONST_16 = + new Dop(Opcodes.CONST_16, Opcodes.CONST, Opcodes.CONST_HIGH16, Form21s.THE_ONE, true); - public static final Dop CMPL_FLOAT = - new Dop(Opcodes.CMPL_FLOAT, Opcodes.CMPL_FLOAT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop CONST = + new Dop(Opcodes.CONST, Opcodes.CONST, Opcodes.NO_NEXT, Form31i.THE_ONE, true); - public static final Dop CMPG_FLOAT = - new Dop(Opcodes.CMPG_FLOAT, Opcodes.CMPG_FLOAT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop CONST_HIGH16 = + new Dop(Opcodes.CONST_HIGH16, Opcodes.CONST, Opcodes.CONST, Form21h.THE_ONE, true); - public static final Dop CMPL_DOUBLE = - new Dop(Opcodes.CMPL_DOUBLE, Opcodes.CMPL_DOUBLE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop CONST_WIDE_16 = new Dop(Opcodes.CONST_WIDE_16, Opcodes.CONST_WIDE, + Opcodes.CONST_WIDE_HIGH16, Form21s.THE_ONE, true); - public static final Dop CMPG_DOUBLE = - new Dop(Opcodes.CMPG_DOUBLE, Opcodes.CMPG_DOUBLE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop CONST_WIDE_32 = + new Dop(Opcodes.CONST_WIDE_32, Opcodes.CONST_WIDE, Opcodes.CONST_WIDE, Form31i.THE_ONE, true); - public static final Dop CMP_LONG = - new Dop(Opcodes.CMP_LONG, Opcodes.CMP_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop CONST_WIDE = + new Dop(Opcodes.CONST_WIDE, Opcodes.CONST_WIDE, Opcodes.NO_NEXT, Form51l.THE_ONE, true); - public static final Dop IF_EQ = - new Dop(Opcodes.IF_EQ, Opcodes.IF_EQ, - Opcodes.NO_NEXT, Form22t.THE_ONE, false); + public static final Dop CONST_WIDE_HIGH16 = new Dop(Opcodes.CONST_WIDE_HIGH16, Opcodes.CONST_WIDE, + Opcodes.CONST_WIDE_32, Form21h.THE_ONE, true); - public static final Dop IF_NE = - new Dop(Opcodes.IF_NE, Opcodes.IF_NE, - Opcodes.NO_NEXT, Form22t.THE_ONE, false); + public static final Dop CONST_STRING = new Dop(Opcodes.CONST_STRING, Opcodes.CONST_STRING, + Opcodes.CONST_STRING_JUMBO, Form21c.THE_ONE, true); - public static final Dop IF_LT = - new Dop(Opcodes.IF_LT, Opcodes.IF_LT, - Opcodes.NO_NEXT, Form22t.THE_ONE, false); + public static final Dop CONST_STRING_JUMBO = new Dop(Opcodes.CONST_STRING_JUMBO, + Opcodes.CONST_STRING, Opcodes.NO_NEXT, Form31c.THE_ONE, true); - public static final Dop IF_GE = - new Dop(Opcodes.IF_GE, Opcodes.IF_GE, - Opcodes.NO_NEXT, Form22t.THE_ONE, false); + public static final Dop CONST_CLASS = + new Dop(Opcodes.CONST_CLASS, Opcodes.CONST_CLASS, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop IF_GT = - new Dop(Opcodes.IF_GT, Opcodes.IF_GT, - Opcodes.NO_NEXT, Form22t.THE_ONE, false); + public static final Dop MONITOR_ENTER = new Dop(Opcodes.MONITOR_ENTER, Opcodes.MONITOR_ENTER, + Opcodes.NO_NEXT, Form11x.THE_ONE, false); - public static final Dop IF_LE = - new Dop(Opcodes.IF_LE, Opcodes.IF_LE, - Opcodes.NO_NEXT, Form22t.THE_ONE, false); + public static final Dop MONITOR_EXIT = + new Dop(Opcodes.MONITOR_EXIT, Opcodes.MONITOR_EXIT, Opcodes.NO_NEXT, Form11x.THE_ONE, false); - public static final Dop IF_EQZ = - new Dop(Opcodes.IF_EQZ, Opcodes.IF_EQZ, - Opcodes.NO_NEXT, Form21t.THE_ONE, false); + public static final Dop CHECK_CAST = + new Dop(Opcodes.CHECK_CAST, Opcodes.CHECK_CAST, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop IF_NEZ = - new Dop(Opcodes.IF_NEZ, Opcodes.IF_NEZ, - Opcodes.NO_NEXT, Form21t.THE_ONE, false); + public static final Dop INSTANCE_OF = + new Dop(Opcodes.INSTANCE_OF, Opcodes.INSTANCE_OF, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop IF_LTZ = - new Dop(Opcodes.IF_LTZ, Opcodes.IF_LTZ, - Opcodes.NO_NEXT, Form21t.THE_ONE, false); + public static final Dop ARRAY_LENGTH = + new Dop(Opcodes.ARRAY_LENGTH, Opcodes.ARRAY_LENGTH, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop IF_GEZ = - new Dop(Opcodes.IF_GEZ, Opcodes.IF_GEZ, - Opcodes.NO_NEXT, Form21t.THE_ONE, false); + public static final Dop NEW_INSTANCE = + new Dop(Opcodes.NEW_INSTANCE, Opcodes.NEW_INSTANCE, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop IF_GTZ = - new Dop(Opcodes.IF_GTZ, Opcodes.IF_GTZ, - Opcodes.NO_NEXT, Form21t.THE_ONE, false); + public static final Dop NEW_ARRAY = + new Dop(Opcodes.NEW_ARRAY, Opcodes.NEW_ARRAY, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop IF_LEZ = - new Dop(Opcodes.IF_LEZ, Opcodes.IF_LEZ, - Opcodes.NO_NEXT, Form21t.THE_ONE, false); + public static final Dop FILLED_NEW_ARRAY = new Dop(Opcodes.FILLED_NEW_ARRAY, + Opcodes.FILLED_NEW_ARRAY, Opcodes.FILLED_NEW_ARRAY_RANGE, Form35c.THE_ONE, false); - public static final Dop AGET = - new Dop(Opcodes.AGET, Opcodes.AGET, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop FILLED_NEW_ARRAY_RANGE = new Dop(Opcodes.FILLED_NEW_ARRAY_RANGE, + Opcodes.FILLED_NEW_ARRAY, Opcodes.NO_NEXT, Form3rc.THE_ONE, false); - public static final Dop AGET_WIDE = - new Dop(Opcodes.AGET_WIDE, Opcodes.AGET_WIDE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop FILL_ARRAY_DATA = new Dop(Opcodes.FILL_ARRAY_DATA, + Opcodes.FILL_ARRAY_DATA, Opcodes.NO_NEXT, Form31t.THE_ONE, false); - public static final Dop AGET_OBJECT = - new Dop(Opcodes.AGET_OBJECT, Opcodes.AGET_OBJECT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop THROW = + new Dop(Opcodes.THROW, Opcodes.THROW, Opcodes.NO_NEXT, Form11x.THE_ONE, false); - public static final Dop AGET_BOOLEAN = - new Dop(Opcodes.AGET_BOOLEAN, Opcodes.AGET_BOOLEAN, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop GOTO = + new Dop(Opcodes.GOTO, Opcodes.GOTO, Opcodes.GOTO_16, Form10t.THE_ONE, false); - public static final Dop AGET_BYTE = - new Dop(Opcodes.AGET_BYTE, Opcodes.AGET_BYTE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop GOTO_16 = + new Dop(Opcodes.GOTO_16, Opcodes.GOTO, Opcodes.GOTO_32, Form20t.THE_ONE, false); - public static final Dop AGET_CHAR = - new Dop(Opcodes.AGET_CHAR, Opcodes.AGET_CHAR, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop GOTO_32 = + new Dop(Opcodes.GOTO_32, Opcodes.GOTO, Opcodes.NO_NEXT, Form30t.THE_ONE, false); - public static final Dop AGET_SHORT = - new Dop(Opcodes.AGET_SHORT, Opcodes.AGET_SHORT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop PACKED_SWITCH = new Dop(Opcodes.PACKED_SWITCH, Opcodes.PACKED_SWITCH, + Opcodes.NO_NEXT, Form31t.THE_ONE, false); - public static final Dop APUT = - new Dop(Opcodes.APUT, Opcodes.APUT, - Opcodes.NO_NEXT, Form23x.THE_ONE, false); + public static final Dop SPARSE_SWITCH = new Dop(Opcodes.SPARSE_SWITCH, Opcodes.SPARSE_SWITCH, + Opcodes.NO_NEXT, Form31t.THE_ONE, false); - public static final Dop APUT_WIDE = - new Dop(Opcodes.APUT_WIDE, Opcodes.APUT_WIDE, - Opcodes.NO_NEXT, Form23x.THE_ONE, false); + public static final Dop CMPL_FLOAT = + new Dop(Opcodes.CMPL_FLOAT, Opcodes.CMPL_FLOAT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop APUT_OBJECT = - new Dop(Opcodes.APUT_OBJECT, Opcodes.APUT_OBJECT, - Opcodes.NO_NEXT, Form23x.THE_ONE, false); + public static final Dop CMPG_FLOAT = + new Dop(Opcodes.CMPG_FLOAT, Opcodes.CMPG_FLOAT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop APUT_BOOLEAN = - new Dop(Opcodes.APUT_BOOLEAN, Opcodes.APUT_BOOLEAN, - Opcodes.NO_NEXT, Form23x.THE_ONE, false); + public static final Dop CMPL_DOUBLE = + new Dop(Opcodes.CMPL_DOUBLE, Opcodes.CMPL_DOUBLE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop APUT_BYTE = - new Dop(Opcodes.APUT_BYTE, Opcodes.APUT_BYTE, - Opcodes.NO_NEXT, Form23x.THE_ONE, false); + public static final Dop CMPG_DOUBLE = + new Dop(Opcodes.CMPG_DOUBLE, Opcodes.CMPG_DOUBLE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop APUT_CHAR = - new Dop(Opcodes.APUT_CHAR, Opcodes.APUT_CHAR, - Opcodes.NO_NEXT, Form23x.THE_ONE, false); + public static final Dop CMP_LONG = + new Dop(Opcodes.CMP_LONG, Opcodes.CMP_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop APUT_SHORT = - new Dop(Opcodes.APUT_SHORT, Opcodes.APUT_SHORT, - Opcodes.NO_NEXT, Form23x.THE_ONE, false); + public static final Dop IF_EQ = + new Dop(Opcodes.IF_EQ, Opcodes.IF_EQ, Opcodes.NO_NEXT, Form22t.THE_ONE, false); - public static final Dop IGET = - new Dop(Opcodes.IGET, Opcodes.IGET, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop IF_NE = + new Dop(Opcodes.IF_NE, Opcodes.IF_NE, Opcodes.NO_NEXT, Form22t.THE_ONE, false); - public static final Dop IGET_WIDE = - new Dop(Opcodes.IGET_WIDE, Opcodes.IGET_WIDE, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop IF_LT = + new Dop(Opcodes.IF_LT, Opcodes.IF_LT, Opcodes.NO_NEXT, Form22t.THE_ONE, false); - public static final Dop IGET_OBJECT = - new Dop(Opcodes.IGET_OBJECT, Opcodes.IGET_OBJECT, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop IF_GE = + new Dop(Opcodes.IF_GE, Opcodes.IF_GE, Opcodes.NO_NEXT, Form22t.THE_ONE, false); - public static final Dop IGET_BOOLEAN = - new Dop(Opcodes.IGET_BOOLEAN, Opcodes.IGET_BOOLEAN, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop IF_GT = + new Dop(Opcodes.IF_GT, Opcodes.IF_GT, Opcodes.NO_NEXT, Form22t.THE_ONE, false); - public static final Dop IGET_BYTE = - new Dop(Opcodes.IGET_BYTE, Opcodes.IGET_BYTE, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop IF_LE = + new Dop(Opcodes.IF_LE, Opcodes.IF_LE, Opcodes.NO_NEXT, Form22t.THE_ONE, false); - public static final Dop IGET_CHAR = - new Dop(Opcodes.IGET_CHAR, Opcodes.IGET_CHAR, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop IF_EQZ = + new Dop(Opcodes.IF_EQZ, Opcodes.IF_EQZ, Opcodes.NO_NEXT, Form21t.THE_ONE, false); - public static final Dop IGET_SHORT = - new Dop(Opcodes.IGET_SHORT, Opcodes.IGET_SHORT, - Opcodes.NO_NEXT, Form22c.THE_ONE, true); + public static final Dop IF_NEZ = + new Dop(Opcodes.IF_NEZ, Opcodes.IF_NEZ, Opcodes.NO_NEXT, Form21t.THE_ONE, false); - public static final Dop IPUT = - new Dop(Opcodes.IPUT, Opcodes.IPUT, - Opcodes.NO_NEXT, Form22c.THE_ONE, false); + public static final Dop IF_LTZ = + new Dop(Opcodes.IF_LTZ, Opcodes.IF_LTZ, Opcodes.NO_NEXT, Form21t.THE_ONE, false); - public static final Dop IPUT_WIDE = - new Dop(Opcodes.IPUT_WIDE, Opcodes.IPUT_WIDE, - Opcodes.NO_NEXT, Form22c.THE_ONE, false); + public static final Dop IF_GEZ = + new Dop(Opcodes.IF_GEZ, Opcodes.IF_GEZ, Opcodes.NO_NEXT, Form21t.THE_ONE, false); - public static final Dop IPUT_OBJECT = - new Dop(Opcodes.IPUT_OBJECT, Opcodes.IPUT_OBJECT, - Opcodes.NO_NEXT, Form22c.THE_ONE, false); + public static final Dop IF_GTZ = + new Dop(Opcodes.IF_GTZ, Opcodes.IF_GTZ, Opcodes.NO_NEXT, Form21t.THE_ONE, false); - public static final Dop IPUT_BOOLEAN = - new Dop(Opcodes.IPUT_BOOLEAN, Opcodes.IPUT_BOOLEAN, - Opcodes.NO_NEXT, Form22c.THE_ONE, false); + public static final Dop IF_LEZ = + new Dop(Opcodes.IF_LEZ, Opcodes.IF_LEZ, Opcodes.NO_NEXT, Form21t.THE_ONE, false); - public static final Dop IPUT_BYTE = - new Dop(Opcodes.IPUT_BYTE, Opcodes.IPUT_BYTE, - Opcodes.NO_NEXT, Form22c.THE_ONE, false); + public static final Dop AGET = + new Dop(Opcodes.AGET, Opcodes.AGET, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop IPUT_CHAR = - new Dop(Opcodes.IPUT_CHAR, Opcodes.IPUT_CHAR, - Opcodes.NO_NEXT, Form22c.THE_ONE, false); + public static final Dop AGET_WIDE = + new Dop(Opcodes.AGET_WIDE, Opcodes.AGET_WIDE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop IPUT_SHORT = - new Dop(Opcodes.IPUT_SHORT, Opcodes.IPUT_SHORT, - Opcodes.NO_NEXT, Form22c.THE_ONE, false); + public static final Dop AGET_OBJECT = + new Dop(Opcodes.AGET_OBJECT, Opcodes.AGET_OBJECT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SGET = - new Dop(Opcodes.SGET, Opcodes.SGET, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop AGET_BOOLEAN = + new Dop(Opcodes.AGET_BOOLEAN, Opcodes.AGET_BOOLEAN, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SGET_WIDE = - new Dop(Opcodes.SGET_WIDE, Opcodes.SGET_WIDE, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop AGET_BYTE = + new Dop(Opcodes.AGET_BYTE, Opcodes.AGET_BYTE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SGET_OBJECT = - new Dop(Opcodes.SGET_OBJECT, Opcodes.SGET_OBJECT, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop AGET_CHAR = + new Dop(Opcodes.AGET_CHAR, Opcodes.AGET_CHAR, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SGET_BOOLEAN = - new Dop(Opcodes.SGET_BOOLEAN, Opcodes.SGET_BOOLEAN, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop AGET_SHORT = + new Dop(Opcodes.AGET_SHORT, Opcodes.AGET_SHORT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SGET_BYTE = - new Dop(Opcodes.SGET_BYTE, Opcodes.SGET_BYTE, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop APUT = + new Dop(Opcodes.APUT, Opcodes.APUT, Opcodes.NO_NEXT, Form23x.THE_ONE, false); - public static final Dop SGET_CHAR = - new Dop(Opcodes.SGET_CHAR, Opcodes.SGET_CHAR, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop APUT_WIDE = + new Dop(Opcodes.APUT_WIDE, Opcodes.APUT_WIDE, Opcodes.NO_NEXT, Form23x.THE_ONE, false); - public static final Dop SGET_SHORT = - new Dop(Opcodes.SGET_SHORT, Opcodes.SGET_SHORT, - Opcodes.NO_NEXT, Form21c.THE_ONE, true); + public static final Dop APUT_OBJECT = + new Dop(Opcodes.APUT_OBJECT, Opcodes.APUT_OBJECT, Opcodes.NO_NEXT, Form23x.THE_ONE, false); - public static final Dop SPUT = - new Dop(Opcodes.SPUT, Opcodes.SPUT, - Opcodes.NO_NEXT, Form21c.THE_ONE, false); + public static final Dop APUT_BOOLEAN = + new Dop(Opcodes.APUT_BOOLEAN, Opcodes.APUT_BOOLEAN, Opcodes.NO_NEXT, Form23x.THE_ONE, false); - public static final Dop SPUT_WIDE = - new Dop(Opcodes.SPUT_WIDE, Opcodes.SPUT_WIDE, - Opcodes.NO_NEXT, Form21c.THE_ONE, false); + public static final Dop APUT_BYTE = + new Dop(Opcodes.APUT_BYTE, Opcodes.APUT_BYTE, Opcodes.NO_NEXT, Form23x.THE_ONE, false); - public static final Dop SPUT_OBJECT = - new Dop(Opcodes.SPUT_OBJECT, Opcodes.SPUT_OBJECT, - Opcodes.NO_NEXT, Form21c.THE_ONE, false); + public static final Dop APUT_CHAR = + new Dop(Opcodes.APUT_CHAR, Opcodes.APUT_CHAR, Opcodes.NO_NEXT, Form23x.THE_ONE, false); - public static final Dop SPUT_BOOLEAN = - new Dop(Opcodes.SPUT_BOOLEAN, Opcodes.SPUT_BOOLEAN, - Opcodes.NO_NEXT, Form21c.THE_ONE, false); + public static final Dop APUT_SHORT = + new Dop(Opcodes.APUT_SHORT, Opcodes.APUT_SHORT, Opcodes.NO_NEXT, Form23x.THE_ONE, false); - public static final Dop SPUT_BYTE = - new Dop(Opcodes.SPUT_BYTE, Opcodes.SPUT_BYTE, - Opcodes.NO_NEXT, Form21c.THE_ONE, false); + public static final Dop IGET = + new Dop(Opcodes.IGET, Opcodes.IGET, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop SPUT_CHAR = - new Dop(Opcodes.SPUT_CHAR, Opcodes.SPUT_CHAR, - Opcodes.NO_NEXT, Form21c.THE_ONE, false); + public static final Dop IGET_WIDE = + new Dop(Opcodes.IGET_WIDE, Opcodes.IGET_WIDE, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop SPUT_SHORT = - new Dop(Opcodes.SPUT_SHORT, Opcodes.SPUT_SHORT, - Opcodes.NO_NEXT, Form21c.THE_ONE, false); + public static final Dop IGET_OBJECT = + new Dop(Opcodes.IGET_OBJECT, Opcodes.IGET_OBJECT, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop INVOKE_VIRTUAL = - new Dop(Opcodes.INVOKE_VIRTUAL, Opcodes.INVOKE_VIRTUAL, - Opcodes.INVOKE_VIRTUAL_RANGE, Form35c.THE_ONE, false); + public static final Dop IGET_BOOLEAN = + new Dop(Opcodes.IGET_BOOLEAN, Opcodes.IGET_BOOLEAN, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop INVOKE_SUPER = - new Dop(Opcodes.INVOKE_SUPER, Opcodes.INVOKE_SUPER, - Opcodes.INVOKE_SUPER_RANGE, Form35c.THE_ONE, false); + public static final Dop IGET_BYTE = + new Dop(Opcodes.IGET_BYTE, Opcodes.IGET_BYTE, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop INVOKE_DIRECT = - new Dop(Opcodes.INVOKE_DIRECT, Opcodes.INVOKE_DIRECT, - Opcodes.INVOKE_DIRECT_RANGE, Form35c.THE_ONE, false); + public static final Dop IGET_CHAR = + new Dop(Opcodes.IGET_CHAR, Opcodes.IGET_CHAR, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop INVOKE_STATIC = - new Dop(Opcodes.INVOKE_STATIC, Opcodes.INVOKE_STATIC, - Opcodes.INVOKE_STATIC_RANGE, Form35c.THE_ONE, false); + public static final Dop IGET_SHORT = + new Dop(Opcodes.IGET_SHORT, Opcodes.IGET_SHORT, Opcodes.NO_NEXT, Form22c.THE_ONE, true); - public static final Dop INVOKE_INTERFACE = - new Dop(Opcodes.INVOKE_INTERFACE, Opcodes.INVOKE_INTERFACE, - Opcodes.INVOKE_INTERFACE_RANGE, Form35c.THE_ONE, false); + public static final Dop IPUT = + new Dop(Opcodes.IPUT, Opcodes.IPUT, Opcodes.NO_NEXT, Form22c.THE_ONE, false); - public static final Dop INVOKE_VIRTUAL_RANGE = - new Dop(Opcodes.INVOKE_VIRTUAL_RANGE, Opcodes.INVOKE_VIRTUAL, - Opcodes.NO_NEXT, Form3rc.THE_ONE, false); + public static final Dop IPUT_WIDE = + new Dop(Opcodes.IPUT_WIDE, Opcodes.IPUT_WIDE, Opcodes.NO_NEXT, Form22c.THE_ONE, false); - public static final Dop INVOKE_SUPER_RANGE = - new Dop(Opcodes.INVOKE_SUPER_RANGE, Opcodes.INVOKE_SUPER, - Opcodes.NO_NEXT, Form3rc.THE_ONE, false); + public static final Dop IPUT_OBJECT = + new Dop(Opcodes.IPUT_OBJECT, Opcodes.IPUT_OBJECT, Opcodes.NO_NEXT, Form22c.THE_ONE, false); - public static final Dop INVOKE_DIRECT_RANGE = - new Dop(Opcodes.INVOKE_DIRECT_RANGE, Opcodes.INVOKE_DIRECT, - Opcodes.NO_NEXT, Form3rc.THE_ONE, false); + public static final Dop IPUT_BOOLEAN = + new Dop(Opcodes.IPUT_BOOLEAN, Opcodes.IPUT_BOOLEAN, Opcodes.NO_NEXT, Form22c.THE_ONE, false); - public static final Dop INVOKE_STATIC_RANGE = - new Dop(Opcodes.INVOKE_STATIC_RANGE, Opcodes.INVOKE_STATIC, - Opcodes.NO_NEXT, Form3rc.THE_ONE, false); + public static final Dop IPUT_BYTE = + new Dop(Opcodes.IPUT_BYTE, Opcodes.IPUT_BYTE, Opcodes.NO_NEXT, Form22c.THE_ONE, false); - public static final Dop INVOKE_INTERFACE_RANGE = - new Dop(Opcodes.INVOKE_INTERFACE_RANGE, Opcodes.INVOKE_INTERFACE, - Opcodes.NO_NEXT, Form3rc.THE_ONE, false); + public static final Dop IPUT_CHAR = + new Dop(Opcodes.IPUT_CHAR, Opcodes.IPUT_CHAR, Opcodes.NO_NEXT, Form22c.THE_ONE, false); - public static final Dop NEG_INT = - new Dop(Opcodes.NEG_INT, Opcodes.NEG_INT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop IPUT_SHORT = + new Dop(Opcodes.IPUT_SHORT, Opcodes.IPUT_SHORT, Opcodes.NO_NEXT, Form22c.THE_ONE, false); - public static final Dop NOT_INT = - new Dop(Opcodes.NOT_INT, Opcodes.NOT_INT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SGET = + new Dop(Opcodes.SGET, Opcodes.SGET, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop NEG_LONG = - new Dop(Opcodes.NEG_LONG, Opcodes.NEG_LONG, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SGET_WIDE = + new Dop(Opcodes.SGET_WIDE, Opcodes.SGET_WIDE, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop NOT_LONG = - new Dop(Opcodes.NOT_LONG, Opcodes.NOT_LONG, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SGET_OBJECT = + new Dop(Opcodes.SGET_OBJECT, Opcodes.SGET_OBJECT, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop NEG_FLOAT = - new Dop(Opcodes.NEG_FLOAT, Opcodes.NEG_FLOAT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SGET_BOOLEAN = + new Dop(Opcodes.SGET_BOOLEAN, Opcodes.SGET_BOOLEAN, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop NEG_DOUBLE = - new Dop(Opcodes.NEG_DOUBLE, Opcodes.NEG_DOUBLE, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SGET_BYTE = + new Dop(Opcodes.SGET_BYTE, Opcodes.SGET_BYTE, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop INT_TO_LONG = - new Dop(Opcodes.INT_TO_LONG, Opcodes.INT_TO_LONG, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SGET_CHAR = + new Dop(Opcodes.SGET_CHAR, Opcodes.SGET_CHAR, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop INT_TO_FLOAT = - new Dop(Opcodes.INT_TO_FLOAT, Opcodes.INT_TO_FLOAT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SGET_SHORT = + new Dop(Opcodes.SGET_SHORT, Opcodes.SGET_SHORT, Opcodes.NO_NEXT, Form21c.THE_ONE, true); - public static final Dop INT_TO_DOUBLE = - new Dop(Opcodes.INT_TO_DOUBLE, Opcodes.INT_TO_DOUBLE, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SPUT = + new Dop(Opcodes.SPUT, Opcodes.SPUT, Opcodes.NO_NEXT, Form21c.THE_ONE, false); - public static final Dop LONG_TO_INT = - new Dop(Opcodes.LONG_TO_INT, Opcodes.LONG_TO_INT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SPUT_WIDE = + new Dop(Opcodes.SPUT_WIDE, Opcodes.SPUT_WIDE, Opcodes.NO_NEXT, Form21c.THE_ONE, false); - public static final Dop LONG_TO_FLOAT = - new Dop(Opcodes.LONG_TO_FLOAT, Opcodes.LONG_TO_FLOAT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SPUT_OBJECT = + new Dop(Opcodes.SPUT_OBJECT, Opcodes.SPUT_OBJECT, Opcodes.NO_NEXT, Form21c.THE_ONE, false); - public static final Dop LONG_TO_DOUBLE = - new Dop(Opcodes.LONG_TO_DOUBLE, Opcodes.LONG_TO_DOUBLE, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SPUT_BOOLEAN = + new Dop(Opcodes.SPUT_BOOLEAN, Opcodes.SPUT_BOOLEAN, Opcodes.NO_NEXT, Form21c.THE_ONE, false); - public static final Dop FLOAT_TO_INT = - new Dop(Opcodes.FLOAT_TO_INT, Opcodes.FLOAT_TO_INT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SPUT_BYTE = + new Dop(Opcodes.SPUT_BYTE, Opcodes.SPUT_BYTE, Opcodes.NO_NEXT, Form21c.THE_ONE, false); - public static final Dop FLOAT_TO_LONG = - new Dop(Opcodes.FLOAT_TO_LONG, Opcodes.FLOAT_TO_LONG, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SPUT_CHAR = + new Dop(Opcodes.SPUT_CHAR, Opcodes.SPUT_CHAR, Opcodes.NO_NEXT, Form21c.THE_ONE, false); - public static final Dop FLOAT_TO_DOUBLE = - new Dop(Opcodes.FLOAT_TO_DOUBLE, Opcodes.FLOAT_TO_DOUBLE, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop SPUT_SHORT = + new Dop(Opcodes.SPUT_SHORT, Opcodes.SPUT_SHORT, Opcodes.NO_NEXT, Form21c.THE_ONE, false); - public static final Dop DOUBLE_TO_INT = - new Dop(Opcodes.DOUBLE_TO_INT, Opcodes.DOUBLE_TO_INT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop INVOKE_VIRTUAL = new Dop(Opcodes.INVOKE_VIRTUAL, Opcodes.INVOKE_VIRTUAL, + Opcodes.INVOKE_VIRTUAL_RANGE, Form35c.THE_ONE, false); - public static final Dop DOUBLE_TO_LONG = - new Dop(Opcodes.DOUBLE_TO_LONG, Opcodes.DOUBLE_TO_LONG, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop INVOKE_SUPER = new Dop(Opcodes.INVOKE_SUPER, Opcodes.INVOKE_SUPER, + Opcodes.INVOKE_SUPER_RANGE, Form35c.THE_ONE, false); - public static final Dop DOUBLE_TO_FLOAT = - new Dop(Opcodes.DOUBLE_TO_FLOAT, Opcodes.DOUBLE_TO_FLOAT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop INVOKE_DIRECT = new Dop(Opcodes.INVOKE_DIRECT, Opcodes.INVOKE_DIRECT, + Opcodes.INVOKE_DIRECT_RANGE, Form35c.THE_ONE, false); - public static final Dop INT_TO_BYTE = - new Dop(Opcodes.INT_TO_BYTE, Opcodes.INT_TO_BYTE, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop INVOKE_STATIC = new Dop(Opcodes.INVOKE_STATIC, Opcodes.INVOKE_STATIC, + Opcodes.INVOKE_STATIC_RANGE, Form35c.THE_ONE, false); - public static final Dop INT_TO_CHAR = - new Dop(Opcodes.INT_TO_CHAR, Opcodes.INT_TO_CHAR, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop INVOKE_INTERFACE = new Dop(Opcodes.INVOKE_INTERFACE, + Opcodes.INVOKE_INTERFACE, Opcodes.INVOKE_INTERFACE_RANGE, Form35c.THE_ONE, false); - public static final Dop INT_TO_SHORT = - new Dop(Opcodes.INT_TO_SHORT, Opcodes.INT_TO_SHORT, - Opcodes.NO_NEXT, Form12x.THE_ONE, true); + public static final Dop INVOKE_VIRTUAL_RANGE = new Dop(Opcodes.INVOKE_VIRTUAL_RANGE, + Opcodes.INVOKE_VIRTUAL, Opcodes.NO_NEXT, Form3rc.THE_ONE, false); - public static final Dop ADD_INT = - new Dop(Opcodes.ADD_INT, Opcodes.ADD_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INVOKE_SUPER_RANGE = new Dop(Opcodes.INVOKE_SUPER_RANGE, + Opcodes.INVOKE_SUPER, Opcodes.NO_NEXT, Form3rc.THE_ONE, false); - public static final Dop SUB_INT = - new Dop(Opcodes.SUB_INT, Opcodes.SUB_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INVOKE_DIRECT_RANGE = new Dop(Opcodes.INVOKE_DIRECT_RANGE, + Opcodes.INVOKE_DIRECT, Opcodes.NO_NEXT, Form3rc.THE_ONE, false); - public static final Dop MUL_INT = - new Dop(Opcodes.MUL_INT, Opcodes.MUL_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INVOKE_STATIC_RANGE = new Dop(Opcodes.INVOKE_STATIC_RANGE, + Opcodes.INVOKE_STATIC, Opcodes.NO_NEXT, Form3rc.THE_ONE, false); - public static final Dop DIV_INT = - new Dop(Opcodes.DIV_INT, Opcodes.DIV_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INVOKE_INTERFACE_RANGE = new Dop(Opcodes.INVOKE_INTERFACE_RANGE, + Opcodes.INVOKE_INTERFACE, Opcodes.NO_NEXT, Form3rc.THE_ONE, false); - public static final Dop REM_INT = - new Dop(Opcodes.REM_INT, Opcodes.REM_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop NEG_INT = + new Dop(Opcodes.NEG_INT, Opcodes.NEG_INT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop AND_INT = - new Dop(Opcodes.AND_INT, Opcodes.AND_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop NOT_INT = + new Dop(Opcodes.NOT_INT, Opcodes.NOT_INT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop OR_INT = - new Dop(Opcodes.OR_INT, Opcodes.OR_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop NEG_LONG = + new Dop(Opcodes.NEG_LONG, Opcodes.NEG_LONG, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop XOR_INT = - new Dop(Opcodes.XOR_INT, Opcodes.XOR_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop NOT_LONG = + new Dop(Opcodes.NOT_LONG, Opcodes.NOT_LONG, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop SHL_INT = - new Dop(Opcodes.SHL_INT, Opcodes.SHL_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop NEG_FLOAT = + new Dop(Opcodes.NEG_FLOAT, Opcodes.NEG_FLOAT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop SHR_INT = - new Dop(Opcodes.SHR_INT, Opcodes.SHR_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop NEG_DOUBLE = + new Dop(Opcodes.NEG_DOUBLE, Opcodes.NEG_DOUBLE, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop USHR_INT = - new Dop(Opcodes.USHR_INT, Opcodes.USHR_INT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INT_TO_LONG = + new Dop(Opcodes.INT_TO_LONG, Opcodes.INT_TO_LONG, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop ADD_LONG = - new Dop(Opcodes.ADD_LONG, Opcodes.ADD_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INT_TO_FLOAT = + new Dop(Opcodes.INT_TO_FLOAT, Opcodes.INT_TO_FLOAT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop SUB_LONG = - new Dop(Opcodes.SUB_LONG, Opcodes.SUB_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INT_TO_DOUBLE = + new Dop(Opcodes.INT_TO_DOUBLE, Opcodes.INT_TO_DOUBLE, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop MUL_LONG = - new Dop(Opcodes.MUL_LONG, Opcodes.MUL_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop LONG_TO_INT = + new Dop(Opcodes.LONG_TO_INT, Opcodes.LONG_TO_INT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop DIV_LONG = - new Dop(Opcodes.DIV_LONG, Opcodes.DIV_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop LONG_TO_FLOAT = + new Dop(Opcodes.LONG_TO_FLOAT, Opcodes.LONG_TO_FLOAT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop REM_LONG = - new Dop(Opcodes.REM_LONG, Opcodes.REM_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop LONG_TO_DOUBLE = new Dop(Opcodes.LONG_TO_DOUBLE, Opcodes.LONG_TO_DOUBLE, + Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop AND_LONG = - new Dop(Opcodes.AND_LONG, Opcodes.AND_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop FLOAT_TO_INT = + new Dop(Opcodes.FLOAT_TO_INT, Opcodes.FLOAT_TO_INT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop OR_LONG = - new Dop(Opcodes.OR_LONG, Opcodes.OR_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop FLOAT_TO_LONG = + new Dop(Opcodes.FLOAT_TO_LONG, Opcodes.FLOAT_TO_LONG, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop XOR_LONG = - new Dop(Opcodes.XOR_LONG, Opcodes.XOR_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop FLOAT_TO_DOUBLE = new Dop(Opcodes.FLOAT_TO_DOUBLE, + Opcodes.FLOAT_TO_DOUBLE, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop SHL_LONG = - new Dop(Opcodes.SHL_LONG, Opcodes.SHL_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop DOUBLE_TO_INT = + new Dop(Opcodes.DOUBLE_TO_INT, Opcodes.DOUBLE_TO_INT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop SHR_LONG = - new Dop(Opcodes.SHR_LONG, Opcodes.SHR_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop DOUBLE_TO_LONG = new Dop(Opcodes.DOUBLE_TO_LONG, Opcodes.DOUBLE_TO_LONG, + Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop USHR_LONG = - new Dop(Opcodes.USHR_LONG, Opcodes.USHR_LONG, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop DOUBLE_TO_FLOAT = new Dop(Opcodes.DOUBLE_TO_FLOAT, + Opcodes.DOUBLE_TO_FLOAT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop ADD_FLOAT = - new Dop(Opcodes.ADD_FLOAT, Opcodes.ADD_FLOAT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INT_TO_BYTE = + new Dop(Opcodes.INT_TO_BYTE, Opcodes.INT_TO_BYTE, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop SUB_FLOAT = - new Dop(Opcodes.SUB_FLOAT, Opcodes.SUB_FLOAT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INT_TO_CHAR = + new Dop(Opcodes.INT_TO_CHAR, Opcodes.INT_TO_CHAR, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop MUL_FLOAT = - new Dop(Opcodes.MUL_FLOAT, Opcodes.MUL_FLOAT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop INT_TO_SHORT = + new Dop(Opcodes.INT_TO_SHORT, Opcodes.INT_TO_SHORT, Opcodes.NO_NEXT, Form12x.THE_ONE, true); - public static final Dop DIV_FLOAT = - new Dop(Opcodes.DIV_FLOAT, Opcodes.DIV_FLOAT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop ADD_INT = + new Dop(Opcodes.ADD_INT, Opcodes.ADD_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop REM_FLOAT = - new Dop(Opcodes.REM_FLOAT, Opcodes.REM_FLOAT, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop SUB_INT = + new Dop(Opcodes.SUB_INT, Opcodes.SUB_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop ADD_DOUBLE = - new Dop(Opcodes.ADD_DOUBLE, Opcodes.ADD_DOUBLE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop MUL_INT = + new Dop(Opcodes.MUL_INT, Opcodes.MUL_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SUB_DOUBLE = - new Dop(Opcodes.SUB_DOUBLE, Opcodes.SUB_DOUBLE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop DIV_INT = + new Dop(Opcodes.DIV_INT, Opcodes.DIV_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop MUL_DOUBLE = - new Dop(Opcodes.MUL_DOUBLE, Opcodes.MUL_DOUBLE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop REM_INT = + new Dop(Opcodes.REM_INT, Opcodes.REM_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop DIV_DOUBLE = - new Dop(Opcodes.DIV_DOUBLE, Opcodes.DIV_DOUBLE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop AND_INT = + new Dop(Opcodes.AND_INT, Opcodes.AND_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop REM_DOUBLE = - new Dop(Opcodes.REM_DOUBLE, Opcodes.REM_DOUBLE, - Opcodes.NO_NEXT, Form23x.THE_ONE, true); + public static final Dop OR_INT = + new Dop(Opcodes.OR_INT, Opcodes.OR_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop ADD_INT_2ADDR = - new Dop(Opcodes.ADD_INT_2ADDR, Opcodes.ADD_INT, - Opcodes.ADD_INT, Form12x.THE_ONE, true); + public static final Dop XOR_INT = + new Dop(Opcodes.XOR_INT, Opcodes.XOR_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SUB_INT_2ADDR = - new Dop(Opcodes.SUB_INT_2ADDR, Opcodes.SUB_INT, - Opcodes.SUB_INT, Form12x.THE_ONE, true); + public static final Dop SHL_INT = + new Dop(Opcodes.SHL_INT, Opcodes.SHL_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop MUL_INT_2ADDR = - new Dop(Opcodes.MUL_INT_2ADDR, Opcodes.MUL_INT, - Opcodes.MUL_INT, Form12x.THE_ONE, true); + public static final Dop SHR_INT = + new Dop(Opcodes.SHR_INT, Opcodes.SHR_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop DIV_INT_2ADDR = - new Dop(Opcodes.DIV_INT_2ADDR, Opcodes.DIV_INT, - Opcodes.DIV_INT, Form12x.THE_ONE, true); + public static final Dop USHR_INT = + new Dop(Opcodes.USHR_INT, Opcodes.USHR_INT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop REM_INT_2ADDR = - new Dop(Opcodes.REM_INT_2ADDR, Opcodes.REM_INT, - Opcodes.REM_INT, Form12x.THE_ONE, true); + public static final Dop ADD_LONG = + new Dop(Opcodes.ADD_LONG, Opcodes.ADD_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop AND_INT_2ADDR = - new Dop(Opcodes.AND_INT_2ADDR, Opcodes.AND_INT, - Opcodes.AND_INT, Form12x.THE_ONE, true); + public static final Dop SUB_LONG = + new Dop(Opcodes.SUB_LONG, Opcodes.SUB_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop OR_INT_2ADDR = - new Dop(Opcodes.OR_INT_2ADDR, Opcodes.OR_INT, - Opcodes.OR_INT, Form12x.THE_ONE, true); + public static final Dop MUL_LONG = + new Dop(Opcodes.MUL_LONG, Opcodes.MUL_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop XOR_INT_2ADDR = - new Dop(Opcodes.XOR_INT_2ADDR, Opcodes.XOR_INT, - Opcodes.XOR_INT, Form12x.THE_ONE, true); + public static final Dop DIV_LONG = + new Dop(Opcodes.DIV_LONG, Opcodes.DIV_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SHL_INT_2ADDR = - new Dop(Opcodes.SHL_INT_2ADDR, Opcodes.SHL_INT, - Opcodes.SHL_INT, Form12x.THE_ONE, true); + public static final Dop REM_LONG = + new Dop(Opcodes.REM_LONG, Opcodes.REM_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SHR_INT_2ADDR = - new Dop(Opcodes.SHR_INT_2ADDR, Opcodes.SHR_INT, - Opcodes.SHR_INT, Form12x.THE_ONE, true); + public static final Dop AND_LONG = + new Dop(Opcodes.AND_LONG, Opcodes.AND_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop USHR_INT_2ADDR = - new Dop(Opcodes.USHR_INT_2ADDR, Opcodes.USHR_INT, - Opcodes.USHR_INT, Form12x.THE_ONE, true); + public static final Dop OR_LONG = + new Dop(Opcodes.OR_LONG, Opcodes.OR_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop ADD_LONG_2ADDR = - new Dop(Opcodes.ADD_LONG_2ADDR, Opcodes.ADD_LONG, - Opcodes.ADD_LONG, Form12x.THE_ONE, true); + public static final Dop XOR_LONG = + new Dop(Opcodes.XOR_LONG, Opcodes.XOR_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SUB_LONG_2ADDR = - new Dop(Opcodes.SUB_LONG_2ADDR, Opcodes.SUB_LONG, - Opcodes.SUB_LONG, Form12x.THE_ONE, true); + public static final Dop SHL_LONG = + new Dop(Opcodes.SHL_LONG, Opcodes.SHL_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop MUL_LONG_2ADDR = - new Dop(Opcodes.MUL_LONG_2ADDR, Opcodes.MUL_LONG, - Opcodes.MUL_LONG, Form12x.THE_ONE, true); + public static final Dop SHR_LONG = + new Dop(Opcodes.SHR_LONG, Opcodes.SHR_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop DIV_LONG_2ADDR = - new Dop(Opcodes.DIV_LONG_2ADDR, Opcodes.DIV_LONG, - Opcodes.DIV_LONG, Form12x.THE_ONE, true); + public static final Dop USHR_LONG = + new Dop(Opcodes.USHR_LONG, Opcodes.USHR_LONG, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop REM_LONG_2ADDR = - new Dop(Opcodes.REM_LONG_2ADDR, Opcodes.REM_LONG, - Opcodes.REM_LONG, Form12x.THE_ONE, true); + public static final Dop ADD_FLOAT = + new Dop(Opcodes.ADD_FLOAT, Opcodes.ADD_FLOAT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop AND_LONG_2ADDR = - new Dop(Opcodes.AND_LONG_2ADDR, Opcodes.AND_LONG, - Opcodes.AND_LONG, Form12x.THE_ONE, true); + public static final Dop SUB_FLOAT = + new Dop(Opcodes.SUB_FLOAT, Opcodes.SUB_FLOAT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop OR_LONG_2ADDR = - new Dop(Opcodes.OR_LONG_2ADDR, Opcodes.OR_LONG, - Opcodes.OR_LONG, Form12x.THE_ONE, true); + public static final Dop MUL_FLOAT = + new Dop(Opcodes.MUL_FLOAT, Opcodes.MUL_FLOAT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop XOR_LONG_2ADDR = - new Dop(Opcodes.XOR_LONG_2ADDR, Opcodes.XOR_LONG, - Opcodes.XOR_LONG, Form12x.THE_ONE, true); + public static final Dop DIV_FLOAT = + new Dop(Opcodes.DIV_FLOAT, Opcodes.DIV_FLOAT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SHL_LONG_2ADDR = - new Dop(Opcodes.SHL_LONG_2ADDR, Opcodes.SHL_LONG, - Opcodes.SHL_LONG, Form12x.THE_ONE, true); + public static final Dop REM_FLOAT = + new Dop(Opcodes.REM_FLOAT, Opcodes.REM_FLOAT, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SHR_LONG_2ADDR = - new Dop(Opcodes.SHR_LONG_2ADDR, Opcodes.SHR_LONG, - Opcodes.SHR_LONG, Form12x.THE_ONE, true); + public static final Dop ADD_DOUBLE = + new Dop(Opcodes.ADD_DOUBLE, Opcodes.ADD_DOUBLE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop USHR_LONG_2ADDR = - new Dop(Opcodes.USHR_LONG_2ADDR, Opcodes.USHR_LONG, - Opcodes.USHR_LONG, Form12x.THE_ONE, true); + public static final Dop SUB_DOUBLE = + new Dop(Opcodes.SUB_DOUBLE, Opcodes.SUB_DOUBLE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop ADD_FLOAT_2ADDR = - new Dop(Opcodes.ADD_FLOAT_2ADDR, Opcodes.ADD_FLOAT, - Opcodes.ADD_FLOAT, Form12x.THE_ONE, true); + public static final Dop MUL_DOUBLE = + new Dop(Opcodes.MUL_DOUBLE, Opcodes.MUL_DOUBLE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop SUB_FLOAT_2ADDR = - new Dop(Opcodes.SUB_FLOAT_2ADDR, Opcodes.SUB_FLOAT, - Opcodes.SUB_FLOAT, Form12x.THE_ONE, true); + public static final Dop DIV_DOUBLE = + new Dop(Opcodes.DIV_DOUBLE, Opcodes.DIV_DOUBLE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop MUL_FLOAT_2ADDR = - new Dop(Opcodes.MUL_FLOAT_2ADDR, Opcodes.MUL_FLOAT, - Opcodes.MUL_FLOAT, Form12x.THE_ONE, true); + public static final Dop REM_DOUBLE = + new Dop(Opcodes.REM_DOUBLE, Opcodes.REM_DOUBLE, Opcodes.NO_NEXT, Form23x.THE_ONE, true); - public static final Dop DIV_FLOAT_2ADDR = - new Dop(Opcodes.DIV_FLOAT_2ADDR, Opcodes.DIV_FLOAT, - Opcodes.DIV_FLOAT, Form12x.THE_ONE, true); + public static final Dop ADD_INT_2ADDR = + new Dop(Opcodes.ADD_INT_2ADDR, Opcodes.ADD_INT, Opcodes.ADD_INT, Form12x.THE_ONE, true); - public static final Dop REM_FLOAT_2ADDR = - new Dop(Opcodes.REM_FLOAT_2ADDR, Opcodes.REM_FLOAT, - Opcodes.REM_FLOAT, Form12x.THE_ONE, true); + public static final Dop SUB_INT_2ADDR = + new Dop(Opcodes.SUB_INT_2ADDR, Opcodes.SUB_INT, Opcodes.SUB_INT, Form12x.THE_ONE, true); - public static final Dop ADD_DOUBLE_2ADDR = - new Dop(Opcodes.ADD_DOUBLE_2ADDR, Opcodes.ADD_DOUBLE, - Opcodes.ADD_DOUBLE, Form12x.THE_ONE, true); + public static final Dop MUL_INT_2ADDR = + new Dop(Opcodes.MUL_INT_2ADDR, Opcodes.MUL_INT, Opcodes.MUL_INT, Form12x.THE_ONE, true); - public static final Dop SUB_DOUBLE_2ADDR = - new Dop(Opcodes.SUB_DOUBLE_2ADDR, Opcodes.SUB_DOUBLE, - Opcodes.SUB_DOUBLE, Form12x.THE_ONE, true); + public static final Dop DIV_INT_2ADDR = + new Dop(Opcodes.DIV_INT_2ADDR, Opcodes.DIV_INT, Opcodes.DIV_INT, Form12x.THE_ONE, true); - public static final Dop MUL_DOUBLE_2ADDR = - new Dop(Opcodes.MUL_DOUBLE_2ADDR, Opcodes.MUL_DOUBLE, - Opcodes.MUL_DOUBLE, Form12x.THE_ONE, true); + public static final Dop REM_INT_2ADDR = + new Dop(Opcodes.REM_INT_2ADDR, Opcodes.REM_INT, Opcodes.REM_INT, Form12x.THE_ONE, true); - public static final Dop DIV_DOUBLE_2ADDR = - new Dop(Opcodes.DIV_DOUBLE_2ADDR, Opcodes.DIV_DOUBLE, - Opcodes.DIV_DOUBLE, Form12x.THE_ONE, true); - - public static final Dop REM_DOUBLE_2ADDR = - new Dop(Opcodes.REM_DOUBLE_2ADDR, Opcodes.REM_DOUBLE, - Opcodes.REM_DOUBLE, Form12x.THE_ONE, true); - - public static final Dop ADD_INT_LIT16 = - new Dop(Opcodes.ADD_INT_LIT16, Opcodes.ADD_INT, - Opcodes.NO_NEXT, Form22s.THE_ONE, true); - - public static final Dop RSUB_INT = - new Dop(Opcodes.RSUB_INT, Opcodes.RSUB_INT, - Opcodes.NO_NEXT, Form22s.THE_ONE, true); - - public static final Dop MUL_INT_LIT16 = - new Dop(Opcodes.MUL_INT_LIT16, Opcodes.MUL_INT, - Opcodes.NO_NEXT, Form22s.THE_ONE, true); - - public static final Dop DIV_INT_LIT16 = - new Dop(Opcodes.DIV_INT_LIT16, Opcodes.DIV_INT, - Opcodes.NO_NEXT, Form22s.THE_ONE, true); - - public static final Dop REM_INT_LIT16 = - new Dop(Opcodes.REM_INT_LIT16, Opcodes.REM_INT, - Opcodes.NO_NEXT, Form22s.THE_ONE, true); - - public static final Dop AND_INT_LIT16 = - new Dop(Opcodes.AND_INT_LIT16, Opcodes.AND_INT, - Opcodes.NO_NEXT, Form22s.THE_ONE, true); - - public static final Dop OR_INT_LIT16 = - new Dop(Opcodes.OR_INT_LIT16, Opcodes.OR_INT, - Opcodes.NO_NEXT, Form22s.THE_ONE, true); - - public static final Dop XOR_INT_LIT16 = - new Dop(Opcodes.XOR_INT_LIT16, Opcodes.XOR_INT, - Opcodes.NO_NEXT, Form22s.THE_ONE, true); - - public static final Dop ADD_INT_LIT8 = - new Dop(Opcodes.ADD_INT_LIT8, Opcodes.ADD_INT, - Opcodes.ADD_INT_LIT16, Form22b.THE_ONE, true); - - public static final Dop RSUB_INT_LIT8 = - new Dop(Opcodes.RSUB_INT_LIT8, Opcodes.RSUB_INT, - Opcodes.RSUB_INT, Form22b.THE_ONE, true); - - public static final Dop MUL_INT_LIT8 = - new Dop(Opcodes.MUL_INT_LIT8, Opcodes.MUL_INT, - Opcodes.MUL_INT_LIT16, Form22b.THE_ONE, true); - - public static final Dop DIV_INT_LIT8 = - new Dop(Opcodes.DIV_INT_LIT8, Opcodes.DIV_INT, - Opcodes.DIV_INT_LIT16, Form22b.THE_ONE, true); - - public static final Dop REM_INT_LIT8 = - new Dop(Opcodes.REM_INT_LIT8, Opcodes.REM_INT, - Opcodes.REM_INT_LIT16, Form22b.THE_ONE, true); - - public static final Dop AND_INT_LIT8 = - new Dop(Opcodes.AND_INT_LIT8, Opcodes.AND_INT, - Opcodes.AND_INT_LIT16, Form22b.THE_ONE, true); - - public static final Dop OR_INT_LIT8 = - new Dop(Opcodes.OR_INT_LIT8, Opcodes.OR_INT, - Opcodes.OR_INT_LIT16, Form22b.THE_ONE, true); - - public static final Dop XOR_INT_LIT8 = - new Dop(Opcodes.XOR_INT_LIT8, Opcodes.XOR_INT, - Opcodes.XOR_INT_LIT16, Form22b.THE_ONE, true); - - public static final Dop SHL_INT_LIT8 = - new Dop(Opcodes.SHL_INT_LIT8, Opcodes.SHL_INT, - Opcodes.NO_NEXT, Form22b.THE_ONE, true); - - public static final Dop SHR_INT_LIT8 = - new Dop(Opcodes.SHR_INT_LIT8, Opcodes.SHR_INT, - Opcodes.NO_NEXT, Form22b.THE_ONE, true); - - public static final Dop USHR_INT_LIT8 = - new Dop(Opcodes.USHR_INT_LIT8, Opcodes.USHR_INT, - Opcodes.NO_NEXT, Form22b.THE_ONE, true); - - // END(dops) - - // Static initialization. - static { - DOPS = new Dop[Opcodes.MAX_VALUE - Opcodes.MIN_VALUE + 1]; - - set(SPECIAL_FORMAT); - - // BEGIN(dops-init); GENERATED AUTOMATICALLY BY opcode-gen - set(NOP); - set(MOVE); - set(MOVE_FROM16); - set(MOVE_16); - set(MOVE_WIDE); - set(MOVE_WIDE_FROM16); - set(MOVE_WIDE_16); - set(MOVE_OBJECT); - set(MOVE_OBJECT_FROM16); - set(MOVE_OBJECT_16); - set(MOVE_RESULT); - set(MOVE_RESULT_WIDE); - set(MOVE_RESULT_OBJECT); - set(MOVE_EXCEPTION); - set(RETURN_VOID); - set(RETURN); - set(RETURN_WIDE); - set(RETURN_OBJECT); - set(CONST_4); - set(CONST_16); - set(CONST); - set(CONST_HIGH16); - set(CONST_WIDE_16); - set(CONST_WIDE_32); - set(CONST_WIDE); - set(CONST_WIDE_HIGH16); - set(CONST_STRING); - set(CONST_STRING_JUMBO); - set(CONST_CLASS); - set(MONITOR_ENTER); - set(MONITOR_EXIT); - set(CHECK_CAST); - set(INSTANCE_OF); - set(ARRAY_LENGTH); - set(NEW_INSTANCE); - set(NEW_ARRAY); - set(FILLED_NEW_ARRAY); - set(FILLED_NEW_ARRAY_RANGE); - set(FILL_ARRAY_DATA); - set(THROW); - set(GOTO); - set(GOTO_16); - set(GOTO_32); - set(PACKED_SWITCH); - set(SPARSE_SWITCH); - set(CMPL_FLOAT); - set(CMPG_FLOAT); - set(CMPL_DOUBLE); - set(CMPG_DOUBLE); - set(CMP_LONG); - set(IF_EQ); - set(IF_NE); - set(IF_LT); - set(IF_GE); - set(IF_GT); - set(IF_LE); - set(IF_EQZ); - set(IF_NEZ); - set(IF_LTZ); - set(IF_GEZ); - set(IF_GTZ); - set(IF_LEZ); - set(AGET); - set(AGET_WIDE); - set(AGET_OBJECT); - set(AGET_BOOLEAN); - set(AGET_BYTE); - set(AGET_CHAR); - set(AGET_SHORT); - set(APUT); - set(APUT_WIDE); - set(APUT_OBJECT); - set(APUT_BOOLEAN); - set(APUT_BYTE); - set(APUT_CHAR); - set(APUT_SHORT); - set(IGET); - set(IGET_WIDE); - set(IGET_OBJECT); - set(IGET_BOOLEAN); - set(IGET_BYTE); - set(IGET_CHAR); - set(IGET_SHORT); - set(IPUT); - set(IPUT_WIDE); - set(IPUT_OBJECT); - set(IPUT_BOOLEAN); - set(IPUT_BYTE); - set(IPUT_CHAR); - set(IPUT_SHORT); - set(SGET); - set(SGET_WIDE); - set(SGET_OBJECT); - set(SGET_BOOLEAN); - set(SGET_BYTE); - set(SGET_CHAR); - set(SGET_SHORT); - set(SPUT); - set(SPUT_WIDE); - set(SPUT_OBJECT); - set(SPUT_BOOLEAN); - set(SPUT_BYTE); - set(SPUT_CHAR); - set(SPUT_SHORT); - set(INVOKE_VIRTUAL); - set(INVOKE_SUPER); - set(INVOKE_DIRECT); - set(INVOKE_STATIC); - set(INVOKE_INTERFACE); - set(INVOKE_VIRTUAL_RANGE); - set(INVOKE_SUPER_RANGE); - set(INVOKE_DIRECT_RANGE); - set(INVOKE_STATIC_RANGE); - set(INVOKE_INTERFACE_RANGE); - set(NEG_INT); - set(NOT_INT); - set(NEG_LONG); - set(NOT_LONG); - set(NEG_FLOAT); - set(NEG_DOUBLE); - set(INT_TO_LONG); - set(INT_TO_FLOAT); - set(INT_TO_DOUBLE); - set(LONG_TO_INT); - set(LONG_TO_FLOAT); - set(LONG_TO_DOUBLE); - set(FLOAT_TO_INT); - set(FLOAT_TO_LONG); - set(FLOAT_TO_DOUBLE); - set(DOUBLE_TO_INT); - set(DOUBLE_TO_LONG); - set(DOUBLE_TO_FLOAT); - set(INT_TO_BYTE); - set(INT_TO_CHAR); - set(INT_TO_SHORT); - set(ADD_INT); - set(SUB_INT); - set(MUL_INT); - set(DIV_INT); - set(REM_INT); - set(AND_INT); - set(OR_INT); - set(XOR_INT); - set(SHL_INT); - set(SHR_INT); - set(USHR_INT); - set(ADD_LONG); - set(SUB_LONG); - set(MUL_LONG); - set(DIV_LONG); - set(REM_LONG); - set(AND_LONG); - set(OR_LONG); - set(XOR_LONG); - set(SHL_LONG); - set(SHR_LONG); - set(USHR_LONG); - set(ADD_FLOAT); - set(SUB_FLOAT); - set(MUL_FLOAT); - set(DIV_FLOAT); - set(REM_FLOAT); - set(ADD_DOUBLE); - set(SUB_DOUBLE); - set(MUL_DOUBLE); - set(DIV_DOUBLE); - set(REM_DOUBLE); - set(ADD_INT_2ADDR); - set(SUB_INT_2ADDR); - set(MUL_INT_2ADDR); - set(DIV_INT_2ADDR); - set(REM_INT_2ADDR); - set(AND_INT_2ADDR); - set(OR_INT_2ADDR); - set(XOR_INT_2ADDR); - set(SHL_INT_2ADDR); - set(SHR_INT_2ADDR); - set(USHR_INT_2ADDR); - set(ADD_LONG_2ADDR); - set(SUB_LONG_2ADDR); - set(MUL_LONG_2ADDR); - set(DIV_LONG_2ADDR); - set(REM_LONG_2ADDR); - set(AND_LONG_2ADDR); - set(OR_LONG_2ADDR); - set(XOR_LONG_2ADDR); - set(SHL_LONG_2ADDR); - set(SHR_LONG_2ADDR); - set(USHR_LONG_2ADDR); - set(ADD_FLOAT_2ADDR); - set(SUB_FLOAT_2ADDR); - set(MUL_FLOAT_2ADDR); - set(DIV_FLOAT_2ADDR); - set(REM_FLOAT_2ADDR); - set(ADD_DOUBLE_2ADDR); - set(SUB_DOUBLE_2ADDR); - set(MUL_DOUBLE_2ADDR); - set(DIV_DOUBLE_2ADDR); - set(REM_DOUBLE_2ADDR); - set(ADD_INT_LIT16); - set(RSUB_INT); - set(MUL_INT_LIT16); - set(DIV_INT_LIT16); - set(REM_INT_LIT16); - set(AND_INT_LIT16); - set(OR_INT_LIT16); - set(XOR_INT_LIT16); - set(ADD_INT_LIT8); - set(RSUB_INT_LIT8); - set(MUL_INT_LIT8); - set(DIV_INT_LIT8); - set(REM_INT_LIT8); - set(AND_INT_LIT8); - set(OR_INT_LIT8); - set(XOR_INT_LIT8); - set(SHL_INT_LIT8); - set(SHR_INT_LIT8); - set(USHR_INT_LIT8); - // END(dops-init) - } + public static final Dop AND_INT_2ADDR = + new Dop(Opcodes.AND_INT_2ADDR, Opcodes.AND_INT, Opcodes.AND_INT, Form12x.THE_ONE, true); - /** - * This class is uninstantiable. - */ - private Dops() { - // This space intentionally left blank. - } + public static final Dop OR_INT_2ADDR = + new Dop(Opcodes.OR_INT_2ADDR, Opcodes.OR_INT, Opcodes.OR_INT, Form12x.THE_ONE, true); - /** - * Gets the {@link Dop} for the given opcode value. - * - * @param opcode {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the - * opcode value - * @return {@code non-null;} the associated opcode instance - */ - public static Dop get(int opcode) { - int idx = opcode - Opcodes.MIN_VALUE; - - try { - Dop result = DOPS[idx]; - if (result != null) { - return result; - } - } catch (ArrayIndexOutOfBoundsException ex) { - // Fall through. - } - - throw new IllegalArgumentException("bogus opcode"); - } + public static final Dop XOR_INT_2ADDR = + new Dop(Opcodes.XOR_INT_2ADDR, Opcodes.XOR_INT, Opcodes.XOR_INT, Form12x.THE_ONE, true); - /** - * Gets the next {@link Dop} in the instruction fitting chain after the - * given instance, if any. - * - * @param opcode {@code non-null;} the opcode - * @param options {@code non-null;} options, used to determine - * which opcodes are potentially off-limits - * @return {@code null-ok;} the next opcode in the same family, in the - * chain of opcodes to try, or {@code null} if the given opcode is - * the last in its chain - */ - public static Dop getNextOrNull(Dop opcode, DexOptions options) { - int nextOpcode = opcode.getNextOpcode(); - - if (nextOpcode == Opcodes.NO_NEXT) { - return null; - } + public static final Dop SHL_INT_2ADDR = + new Dop(Opcodes.SHL_INT_2ADDR, Opcodes.SHL_INT, Opcodes.SHL_INT, Form12x.THE_ONE, true); + + public static final Dop SHR_INT_2ADDR = + new Dop(Opcodes.SHR_INT_2ADDR, Opcodes.SHR_INT, Opcodes.SHR_INT, Form12x.THE_ONE, true); + + public static final Dop USHR_INT_2ADDR = + new Dop(Opcodes.USHR_INT_2ADDR, Opcodes.USHR_INT, Opcodes.USHR_INT, Form12x.THE_ONE, true); + + public static final Dop ADD_LONG_2ADDR = + new Dop(Opcodes.ADD_LONG_2ADDR, Opcodes.ADD_LONG, Opcodes.ADD_LONG, Form12x.THE_ONE, true); + + public static final Dop SUB_LONG_2ADDR = + new Dop(Opcodes.SUB_LONG_2ADDR, Opcodes.SUB_LONG, Opcodes.SUB_LONG, Form12x.THE_ONE, true); + + public static final Dop MUL_LONG_2ADDR = + new Dop(Opcodes.MUL_LONG_2ADDR, Opcodes.MUL_LONG, Opcodes.MUL_LONG, Form12x.THE_ONE, true); + + public static final Dop DIV_LONG_2ADDR = + new Dop(Opcodes.DIV_LONG_2ADDR, Opcodes.DIV_LONG, Opcodes.DIV_LONG, Form12x.THE_ONE, true); + + public static final Dop REM_LONG_2ADDR = + new Dop(Opcodes.REM_LONG_2ADDR, Opcodes.REM_LONG, Opcodes.REM_LONG, Form12x.THE_ONE, true); + + public static final Dop AND_LONG_2ADDR = + new Dop(Opcodes.AND_LONG_2ADDR, Opcodes.AND_LONG, Opcodes.AND_LONG, Form12x.THE_ONE, true); + + public static final Dop OR_LONG_2ADDR = + new Dop(Opcodes.OR_LONG_2ADDR, Opcodes.OR_LONG, Opcodes.OR_LONG, Form12x.THE_ONE, true); + + public static final Dop XOR_LONG_2ADDR = + new Dop(Opcodes.XOR_LONG_2ADDR, Opcodes.XOR_LONG, Opcodes.XOR_LONG, Form12x.THE_ONE, true); + + public static final Dop SHL_LONG_2ADDR = + new Dop(Opcodes.SHL_LONG_2ADDR, Opcodes.SHL_LONG, Opcodes.SHL_LONG, Form12x.THE_ONE, true); + + public static final Dop SHR_LONG_2ADDR = + new Dop(Opcodes.SHR_LONG_2ADDR, Opcodes.SHR_LONG, Opcodes.SHR_LONG, Form12x.THE_ONE, true); + + public static final Dop USHR_LONG_2ADDR = + new Dop(Opcodes.USHR_LONG_2ADDR, Opcodes.USHR_LONG, Opcodes.USHR_LONG, Form12x.THE_ONE, true); + + public static final Dop ADD_FLOAT_2ADDR = + new Dop(Opcodes.ADD_FLOAT_2ADDR, Opcodes.ADD_FLOAT, Opcodes.ADD_FLOAT, Form12x.THE_ONE, true); - opcode = get(nextOpcode); + public static final Dop SUB_FLOAT_2ADDR = + new Dop(Opcodes.SUB_FLOAT_2ADDR, Opcodes.SUB_FLOAT, Opcodes.SUB_FLOAT, Form12x.THE_ONE, true); - return opcode; + public static final Dop MUL_FLOAT_2ADDR = + new Dop(Opcodes.MUL_FLOAT_2ADDR, Opcodes.MUL_FLOAT, Opcodes.MUL_FLOAT, Form12x.THE_ONE, true); + + public static final Dop DIV_FLOAT_2ADDR = + new Dop(Opcodes.DIV_FLOAT_2ADDR, Opcodes.DIV_FLOAT, Opcodes.DIV_FLOAT, Form12x.THE_ONE, true); + + public static final Dop REM_FLOAT_2ADDR = + new Dop(Opcodes.REM_FLOAT_2ADDR, Opcodes.REM_FLOAT, Opcodes.REM_FLOAT, Form12x.THE_ONE, true); + + public static final Dop ADD_DOUBLE_2ADDR = new Dop(Opcodes.ADD_DOUBLE_2ADDR, Opcodes.ADD_DOUBLE, + Opcodes.ADD_DOUBLE, Form12x.THE_ONE, true); + + public static final Dop SUB_DOUBLE_2ADDR = new Dop(Opcodes.SUB_DOUBLE_2ADDR, Opcodes.SUB_DOUBLE, + Opcodes.SUB_DOUBLE, Form12x.THE_ONE, true); + + public static final Dop MUL_DOUBLE_2ADDR = new Dop(Opcodes.MUL_DOUBLE_2ADDR, Opcodes.MUL_DOUBLE, + Opcodes.MUL_DOUBLE, Form12x.THE_ONE, true); + + public static final Dop DIV_DOUBLE_2ADDR = new Dop(Opcodes.DIV_DOUBLE_2ADDR, Opcodes.DIV_DOUBLE, + Opcodes.DIV_DOUBLE, Form12x.THE_ONE, true); + + public static final Dop REM_DOUBLE_2ADDR = new Dop(Opcodes.REM_DOUBLE_2ADDR, Opcodes.REM_DOUBLE, + Opcodes.REM_DOUBLE, Form12x.THE_ONE, true); + + public static final Dop ADD_INT_LIT16 = + new Dop(Opcodes.ADD_INT_LIT16, Opcodes.ADD_INT, Opcodes.NO_NEXT, Form22s.THE_ONE, true); + + public static final Dop RSUB_INT = + new Dop(Opcodes.RSUB_INT, Opcodes.RSUB_INT, Opcodes.NO_NEXT, Form22s.THE_ONE, true); + + public static final Dop MUL_INT_LIT16 = + new Dop(Opcodes.MUL_INT_LIT16, Opcodes.MUL_INT, Opcodes.NO_NEXT, Form22s.THE_ONE, true); + + public static final Dop DIV_INT_LIT16 = + new Dop(Opcodes.DIV_INT_LIT16, Opcodes.DIV_INT, Opcodes.NO_NEXT, Form22s.THE_ONE, true); + + public static final Dop REM_INT_LIT16 = + new Dop(Opcodes.REM_INT_LIT16, Opcodes.REM_INT, Opcodes.NO_NEXT, Form22s.THE_ONE, true); + + public static final Dop AND_INT_LIT16 = + new Dop(Opcodes.AND_INT_LIT16, Opcodes.AND_INT, Opcodes.NO_NEXT, Form22s.THE_ONE, true); + + public static final Dop OR_INT_LIT16 = + new Dop(Opcodes.OR_INT_LIT16, Opcodes.OR_INT, Opcodes.NO_NEXT, Form22s.THE_ONE, true); + + public static final Dop XOR_INT_LIT16 = + new Dop(Opcodes.XOR_INT_LIT16, Opcodes.XOR_INT, Opcodes.NO_NEXT, Form22s.THE_ONE, true); + + public static final Dop ADD_INT_LIT8 = + new Dop(Opcodes.ADD_INT_LIT8, Opcodes.ADD_INT, Opcodes.ADD_INT_LIT16, Form22b.THE_ONE, true); + + public static final Dop RSUB_INT_LIT8 = + new Dop(Opcodes.RSUB_INT_LIT8, Opcodes.RSUB_INT, Opcodes.RSUB_INT, Form22b.THE_ONE, true); + + public static final Dop MUL_INT_LIT8 = + new Dop(Opcodes.MUL_INT_LIT8, Opcodes.MUL_INT, Opcodes.MUL_INT_LIT16, Form22b.THE_ONE, true); + + public static final Dop DIV_INT_LIT8 = + new Dop(Opcodes.DIV_INT_LIT8, Opcodes.DIV_INT, Opcodes.DIV_INT_LIT16, Form22b.THE_ONE, true); + + public static final Dop REM_INT_LIT8 = + new Dop(Opcodes.REM_INT_LIT8, Opcodes.REM_INT, Opcodes.REM_INT_LIT16, Form22b.THE_ONE, true); + + public static final Dop AND_INT_LIT8 = + new Dop(Opcodes.AND_INT_LIT8, Opcodes.AND_INT, Opcodes.AND_INT_LIT16, Form22b.THE_ONE, true); + + public static final Dop OR_INT_LIT8 = + new Dop(Opcodes.OR_INT_LIT8, Opcodes.OR_INT, Opcodes.OR_INT_LIT16, Form22b.THE_ONE, true); + + public static final Dop XOR_INT_LIT8 = + new Dop(Opcodes.XOR_INT_LIT8, Opcodes.XOR_INT, Opcodes.XOR_INT_LIT16, Form22b.THE_ONE, true); + + public static final Dop SHL_INT_LIT8 = + new Dop(Opcodes.SHL_INT_LIT8, Opcodes.SHL_INT, Opcodes.NO_NEXT, Form22b.THE_ONE, true); + + public static final Dop SHR_INT_LIT8 = + new Dop(Opcodes.SHR_INT_LIT8, Opcodes.SHR_INT, Opcodes.NO_NEXT, Form22b.THE_ONE, true); + + public static final Dop USHR_INT_LIT8 = + new Dop(Opcodes.USHR_INT_LIT8, Opcodes.USHR_INT, Opcodes.NO_NEXT, Form22b.THE_ONE, true); + + // END(dops) + + // Static initialization. + static { + DOPS = new Dop[Opcodes.MAX_VALUE - Opcodes.MIN_VALUE + 1]; + + set(SPECIAL_FORMAT); + + // BEGIN(dops-init); GENERATED AUTOMATICALLY BY opcode-gen + set(NOP); + set(MOVE); + set(MOVE_FROM16); + set(MOVE_16); + set(MOVE_WIDE); + set(MOVE_WIDE_FROM16); + set(MOVE_WIDE_16); + set(MOVE_OBJECT); + set(MOVE_OBJECT_FROM16); + set(MOVE_OBJECT_16); + set(MOVE_RESULT); + set(MOVE_RESULT_WIDE); + set(MOVE_RESULT_OBJECT); + set(MOVE_EXCEPTION); + set(RETURN_VOID); + set(RETURN); + set(RETURN_WIDE); + set(RETURN_OBJECT); + set(CONST_4); + set(CONST_16); + set(CONST); + set(CONST_HIGH16); + set(CONST_WIDE_16); + set(CONST_WIDE_32); + set(CONST_WIDE); + set(CONST_WIDE_HIGH16); + set(CONST_STRING); + set(CONST_STRING_JUMBO); + set(CONST_CLASS); + set(MONITOR_ENTER); + set(MONITOR_EXIT); + set(CHECK_CAST); + set(INSTANCE_OF); + set(ARRAY_LENGTH); + set(NEW_INSTANCE); + set(NEW_ARRAY); + set(FILLED_NEW_ARRAY); + set(FILLED_NEW_ARRAY_RANGE); + set(FILL_ARRAY_DATA); + set(THROW); + set(GOTO); + set(GOTO_16); + set(GOTO_32); + set(PACKED_SWITCH); + set(SPARSE_SWITCH); + set(CMPL_FLOAT); + set(CMPG_FLOAT); + set(CMPL_DOUBLE); + set(CMPG_DOUBLE); + set(CMP_LONG); + set(IF_EQ); + set(IF_NE); + set(IF_LT); + set(IF_GE); + set(IF_GT); + set(IF_LE); + set(IF_EQZ); + set(IF_NEZ); + set(IF_LTZ); + set(IF_GEZ); + set(IF_GTZ); + set(IF_LEZ); + set(AGET); + set(AGET_WIDE); + set(AGET_OBJECT); + set(AGET_BOOLEAN); + set(AGET_BYTE); + set(AGET_CHAR); + set(AGET_SHORT); + set(APUT); + set(APUT_WIDE); + set(APUT_OBJECT); + set(APUT_BOOLEAN); + set(APUT_BYTE); + set(APUT_CHAR); + set(APUT_SHORT); + set(IGET); + set(IGET_WIDE); + set(IGET_OBJECT); + set(IGET_BOOLEAN); + set(IGET_BYTE); + set(IGET_CHAR); + set(IGET_SHORT); + set(IPUT); + set(IPUT_WIDE); + set(IPUT_OBJECT); + set(IPUT_BOOLEAN); + set(IPUT_BYTE); + set(IPUT_CHAR); + set(IPUT_SHORT); + set(SGET); + set(SGET_WIDE); + set(SGET_OBJECT); + set(SGET_BOOLEAN); + set(SGET_BYTE); + set(SGET_CHAR); + set(SGET_SHORT); + set(SPUT); + set(SPUT_WIDE); + set(SPUT_OBJECT); + set(SPUT_BOOLEAN); + set(SPUT_BYTE); + set(SPUT_CHAR); + set(SPUT_SHORT); + set(INVOKE_VIRTUAL); + set(INVOKE_SUPER); + set(INVOKE_DIRECT); + set(INVOKE_STATIC); + set(INVOKE_INTERFACE); + set(INVOKE_VIRTUAL_RANGE); + set(INVOKE_SUPER_RANGE); + set(INVOKE_DIRECT_RANGE); + set(INVOKE_STATIC_RANGE); + set(INVOKE_INTERFACE_RANGE); + set(NEG_INT); + set(NOT_INT); + set(NEG_LONG); + set(NOT_LONG); + set(NEG_FLOAT); + set(NEG_DOUBLE); + set(INT_TO_LONG); + set(INT_TO_FLOAT); + set(INT_TO_DOUBLE); + set(LONG_TO_INT); + set(LONG_TO_FLOAT); + set(LONG_TO_DOUBLE); + set(FLOAT_TO_INT); + set(FLOAT_TO_LONG); + set(FLOAT_TO_DOUBLE); + set(DOUBLE_TO_INT); + set(DOUBLE_TO_LONG); + set(DOUBLE_TO_FLOAT); + set(INT_TO_BYTE); + set(INT_TO_CHAR); + set(INT_TO_SHORT); + set(ADD_INT); + set(SUB_INT); + set(MUL_INT); + set(DIV_INT); + set(REM_INT); + set(AND_INT); + set(OR_INT); + set(XOR_INT); + set(SHL_INT); + set(SHR_INT); + set(USHR_INT); + set(ADD_LONG); + set(SUB_LONG); + set(MUL_LONG); + set(DIV_LONG); + set(REM_LONG); + set(AND_LONG); + set(OR_LONG); + set(XOR_LONG); + set(SHL_LONG); + set(SHR_LONG); + set(USHR_LONG); + set(ADD_FLOAT); + set(SUB_FLOAT); + set(MUL_FLOAT); + set(DIV_FLOAT); + set(REM_FLOAT); + set(ADD_DOUBLE); + set(SUB_DOUBLE); + set(MUL_DOUBLE); + set(DIV_DOUBLE); + set(REM_DOUBLE); + set(ADD_INT_2ADDR); + set(SUB_INT_2ADDR); + set(MUL_INT_2ADDR); + set(DIV_INT_2ADDR); + set(REM_INT_2ADDR); + set(AND_INT_2ADDR); + set(OR_INT_2ADDR); + set(XOR_INT_2ADDR); + set(SHL_INT_2ADDR); + set(SHR_INT_2ADDR); + set(USHR_INT_2ADDR); + set(ADD_LONG_2ADDR); + set(SUB_LONG_2ADDR); + set(MUL_LONG_2ADDR); + set(DIV_LONG_2ADDR); + set(REM_LONG_2ADDR); + set(AND_LONG_2ADDR); + set(OR_LONG_2ADDR); + set(XOR_LONG_2ADDR); + set(SHL_LONG_2ADDR); + set(SHR_LONG_2ADDR); + set(USHR_LONG_2ADDR); + set(ADD_FLOAT_2ADDR); + set(SUB_FLOAT_2ADDR); + set(MUL_FLOAT_2ADDR); + set(DIV_FLOAT_2ADDR); + set(REM_FLOAT_2ADDR); + set(ADD_DOUBLE_2ADDR); + set(SUB_DOUBLE_2ADDR); + set(MUL_DOUBLE_2ADDR); + set(DIV_DOUBLE_2ADDR); + set(REM_DOUBLE_2ADDR); + set(ADD_INT_LIT16); + set(RSUB_INT); + set(MUL_INT_LIT16); + set(DIV_INT_LIT16); + set(REM_INT_LIT16); + set(AND_INT_LIT16); + set(OR_INT_LIT16); + set(XOR_INT_LIT16); + set(ADD_INT_LIT8); + set(RSUB_INT_LIT8); + set(MUL_INT_LIT8); + set(DIV_INT_LIT8); + set(REM_INT_LIT8); + set(AND_INT_LIT8); + set(OR_INT_LIT8); + set(XOR_INT_LIT8); + set(SHL_INT_LIT8); + set(SHR_INT_LIT8); + set(USHR_INT_LIT8); + // END(dops-init) + } + + /** + * This class is uninstantiable. + */ + private Dops() { + // This space intentionally left blank. + } + + /** + * Gets the {@link Dop} for the given opcode value. + * + * @param opcode {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the + * opcode value + * @return {@code non-null;} the associated opcode instance + */ + public static Dop get(int opcode) { + int idx = opcode - Opcodes.MIN_VALUE; + + try { + Dop result = DOPS[idx]; + if (result != null) { + return result; + } + } catch (ArrayIndexOutOfBoundsException ex) { + // Fall through. } - /** - * Puts the given opcode into the table of all ops. - * - * @param opcode {@code non-null;} the opcode - */ - private static void set(Dop opcode) { - int idx = opcode.getOpcode() - Opcodes.MIN_VALUE; - DOPS[idx] = opcode; + throw new IllegalArgumentException("bogus opcode"); + } + + /** + * Gets the next {@link Dop} in the instruction fitting chain after the + * given instance, if any. + * + * @param opcode {@code non-null;} the opcode + * @param options {@code non-null;} options, used to determine + * which opcodes are potentially off-limits + * @return {@code null-ok;} the next opcode in the same family, in the + * chain of opcodes to try, or {@code null} if the given opcode is + * the last in its chain + */ + public static Dop getNextOrNull(Dop opcode, DexOptions options) { + int nextOpcode = opcode.getNextOpcode(); + + if (nextOpcode == Opcodes.NO_NEXT) { + return null; } + + opcode = get(nextOpcode); + + return opcode; + } + + /** + * Puts the given opcode into the table of all ops. + * + * @param opcode {@code non-null;} the opcode + */ + private static void set(Dop opcode) { + int idx = opcode.getOpcode() - Opcodes.MIN_VALUE; + DOPS[idx] = opcode; + } } diff --git a/dx/src/com/android/jack/dx/dex/code/FixedSizeInsn.java b/dx/src/com/android/jack/dx/dex/code/FixedSizeInsn.java index d3ab83c..5718b61 100644 --- a/dx/src/com/android/jack/dx/dex/code/FixedSizeInsn.java +++ b/dx/src/com/android/jack/dx/dex/code/FixedSizeInsn.java @@ -26,48 +26,47 @@ import com.android.jack.dx.util.AnnotatedOutput; * includes most — but not all — instructions. */ public abstract class FixedSizeInsn extends DalvInsn { - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * <p><b>Note:</b> In the unlikely event that an instruction takes - * absolutely no registers (e.g., a {@code nop} or a - * no-argument no-result * static method call), then the given - * register list may be passed as {@link - * RegisterSpecList#EMPTY}.</p> - * - * @param opcode the opcode; one of the constants from {@link Dops} - * @param position {@code non-null;} source position - * @param registers {@code non-null;} register list, including a - * result register if appropriate (that is, registers may be either - * ins or outs) - */ - public FixedSizeInsn(Dop opcode, SourcePosition position, - RegisterSpecList registers) { - super(opcode, position, registers); - } + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * <p><b>Note:</b> In the unlikely event that an instruction takes + * absolutely no registers (e.g., a {@code nop} or a + * no-argument no-result * static method call), then the given + * register list may be passed as {@link + * RegisterSpecList#EMPTY}.</p> + * + * @param opcode the opcode; one of the constants from {@link Dops} + * @param position {@code non-null;} source position + * @param registers {@code non-null;} register list, including a + * result register if appropriate (that is, registers may be either + * ins or outs) + */ + public FixedSizeInsn(Dop opcode, SourcePosition position, RegisterSpecList registers) { + super(opcode, position, registers); + } - /** {@inheritDoc} */ - @Override - public final int codeSize() { - return getOpcode().getFormat().codeSize(); - } + /** {@inheritDoc} */ + @Override + public final int codeSize() { + return getOpcode().getFormat().codeSize(); + } - /** {@inheritDoc} */ - @Override - public final void writeTo(AnnotatedOutput out) { - getOpcode().getFormat().writeTo(out, this); - } + /** {@inheritDoc} */ + @Override + public final void writeTo(AnnotatedOutput out) { + getOpcode().getFormat().writeTo(out, this); + } - /** {@inheritDoc} */ - @Override - public final DalvInsn withRegisterOffset(int delta) { - return withRegisters(getRegisters().withOffset(delta)); - } + /** {@inheritDoc} */ + @Override + public final DalvInsn withRegisterOffset(int delta) { + return withRegisters(getRegisters().withOffset(delta)); + } - /** {@inheritDoc} */ - @Override - protected final String listingString0(boolean noteIndices) { - return getOpcode().getFormat().listingString(this, noteIndices); - } + /** {@inheritDoc} */ + @Override + protected final String listingString0(boolean noteIndices) { + return getOpcode().getFormat().listingString(this, noteIndices); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/HighRegisterPrefix.java b/dx/src/com/android/jack/dx/dex/code/HighRegisterPrefix.java index 424db5e..8f9b185 100644 --- a/dx/src/com/android/jack/dx/dex/code/HighRegisterPrefix.java +++ b/dx/src/com/android/jack/dx/dex/code/HighRegisterPrefix.java @@ -19,7 +19,6 @@ package com.android.jack.dx.dex.code; import com.android.jack.dx.rop.code.RegisterSpec; import com.android.jack.dx.rop.code.RegisterSpecList; import com.android.jack.dx.rop.code.SourcePosition; -import com.android.jack.dx.rop.type.Type; import com.android.jack.dx.util.AnnotatedOutput; /** @@ -30,118 +29,116 @@ import com.android.jack.dx.util.AnnotatedOutput; * be met using a straightforward choice of a single opcode. */ public final class HighRegisterPrefix extends VariableSizeInsn { - /** {@code null-ok;} cached instructions, if constructed */ - private SimpleInsn[] insns; - - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - * @param registers {@code non-null;} source registers - */ - public HighRegisterPrefix(SourcePosition position, - RegisterSpecList registers) { - super(position, registers); - - if (registers.size() == 0) { - throw new IllegalArgumentException("registers.size() == 0"); - } - - insns = null; + /** {@code null-ok;} cached instructions, if constructed */ + private SimpleInsn[] insns; + + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + * @param registers {@code non-null;} source registers + */ + public HighRegisterPrefix(SourcePosition position, RegisterSpecList registers) { + super(position, registers); + + if (registers.size() == 0) { + throw new IllegalArgumentException("registers.size() == 0"); } - /** {@inheritDoc} */ - @Override - public int codeSize() { - int result = 0; + insns = null; + } - calculateInsnsIfNecessary(); + /** {@inheritDoc} */ + @Override + public int codeSize() { + int result = 0; - for (SimpleInsn insn : insns) { - result += insn.codeSize(); - } + calculateInsnsIfNecessary(); - return result; + for (SimpleInsn insn : insns) { + result += insn.codeSize(); } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out) { - calculateInsnsIfNecessary(); + return result; + } - for (SimpleInsn insn : insns) { - insn.writeTo(out); - } - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out) { + calculateInsnsIfNecessary(); - /** - * Helper for {@link #codeSize} and {@link #writeTo} which sets up - * {@link #insns} if not already done. - */ - private void calculateInsnsIfNecessary() { - if (insns != null) { - return; - } - - RegisterSpecList registers = getRegisters(); - int sz = registers.size(); - - insns = new SimpleInsn[sz]; - - for (int i = 0, outAt = 0; i < sz; i++) { - RegisterSpec src = registers.get(i); - insns[i] = moveInsnFor(src, outAt); - outAt += src.getCategory(); - } + for (SimpleInsn insn : insns) { + insn.writeTo(out); } - - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new HighRegisterPrefix(getPosition(), registers); + } + + /** + * Helper for {@link #codeSize} and {@link #writeTo} which sets up + * {@link #insns} if not already done. + */ + private void calculateInsnsIfNecessary() { + if (insns != null) { + return; } - /** {@inheritDoc} */ - @Override - protected String argString() { - return null; - } - - /** {@inheritDoc} */ - @Override - protected String listingString0(boolean noteIndices) { - RegisterSpecList registers = getRegisters(); - int sz = registers.size(); - StringBuffer sb = new StringBuffer(100); - - for (int i = 0, outAt = 0; i < sz; i++) { - RegisterSpec src = registers.get(i); - SimpleInsn insn = moveInsnFor(src, outAt); + RegisterSpecList registers = getRegisters(); + int sz = registers.size(); - if (i != 0) { - sb.append('\n'); - } + insns = new SimpleInsn[sz]; - sb.append(insn.listingString0(noteIndices)); - - outAt += src.getCategory(); - } - - return sb.toString(); + for (int i = 0, outAt = 0; i < sz; i++) { + RegisterSpec src = registers.get(i); + insns[i] = moveInsnFor(src, outAt); + outAt += src.getCategory(); } - - /** - * Returns the proper move instruction for the given source spec - * and destination index. - * - * @param src {@code non-null;} the source register spec - * @param destIndex {@code >= 0;} the destination register index - * @return {@code non-null;} the appropriate move instruction - */ - private static SimpleInsn moveInsnFor(RegisterSpec src, int destIndex) { - return DalvInsn.makeMove(SourcePosition.NO_INFO, - RegisterSpec.make(destIndex, src.getType()), - src); + } + + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new HighRegisterPrefix(getPosition(), registers); + } + + /** {@inheritDoc} */ + @Override + protected String argString() { + return null; + } + + /** {@inheritDoc} */ + @Override + protected String listingString0(boolean noteIndices) { + RegisterSpecList registers = getRegisters(); + int sz = registers.size(); + StringBuffer sb = new StringBuffer(100); + + for (int i = 0, outAt = 0; i < sz; i++) { + RegisterSpec src = registers.get(i); + SimpleInsn insn = moveInsnFor(src, outAt); + + if (i != 0) { + sb.append('\n'); + } + + sb.append(insn.listingString0(noteIndices)); + + outAt += src.getCategory(); } + + return sb.toString(); + } + + /** + * Returns the proper move instruction for the given source spec + * and destination index. + * + * @param src {@code non-null;} the source register spec + * @param destIndex {@code >= 0;} the destination register index + * @return {@code non-null;} the appropriate move instruction + */ + private static SimpleInsn moveInsnFor(RegisterSpec src, int destIndex) { + return DalvInsn.makeMove(SourcePosition.NO_INFO, RegisterSpec.make(destIndex, src.getType()), + src); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/InsnFormat.java b/dx/src/com/android/jack/dx/dex/code/InsnFormat.java index ab09b69..276d573 100644 --- a/dx/src/com/android/jack/dx/dex/code/InsnFormat.java +++ b/dx/src/com/android/jack/dx/dex/code/InsnFormat.java @@ -36,679 +36,691 @@ import java.util.BitSet; * representing such translations. */ public abstract class InsnFormat { - /** - * flag to enable/disable the new extended opcode formats; meant as a - * temporary measure until VM support for the salient opcodes is - * added. TODO: Remove this declaration when the VM can deal. - */ - public static boolean ALLOW_EXTENDED_OPCODES = true; - - /** - * Returns the string form, suitable for inclusion in a listing - * dump, of the given instruction. The instruction must be of this - * instance's format for proper operation. - * - * @param insn {@code non-null;} the instruction - * @param noteIndices whether to include an explicit notation of - * constant pool indices - * @return {@code non-null;} the string form - */ - public final String listingString(DalvInsn insn, boolean noteIndices) { - String op = insn.getOpcode().getName(); - String arg = insnArgString(insn); - String comment = insnCommentString(insn, noteIndices); - StringBuilder sb = new StringBuilder(100); - - sb.append(op); - - if (arg.length() != 0) { - sb.append(' '); - sb.append(arg); + /** + * flag to enable/disable the new extended opcode formats; meant as a + * temporary measure until VM support for the salient opcodes is + * added. TODO(dx team): Remove this declaration when the VM can deal. + */ + public static final boolean ALLOW_EXTENDED_OPCODES = true; + + /** + * Returns the string form, suitable for inclusion in a listing + * dump, of the given instruction. The instruction must be of this + * instance's format for proper operation. + * + * @param insn {@code non-null;} the instruction + * @param noteIndices whether to include an explicit notation of + * constant pool indices + * @return {@code non-null;} the string form + */ + public final String listingString(DalvInsn insn, boolean noteIndices) { + String op = insn.getOpcode().getName(); + String arg = insnArgString(insn); + String comment = insnCommentString(insn, noteIndices); + StringBuilder sb = new StringBuilder(100); + + sb.append(op); + + if (arg.length() != 0) { + sb.append(' '); + sb.append(arg); + } + + if (comment.length() != 0) { + sb.append(" // "); + sb.append(comment); + } + + return sb.toString(); + } + + /** + * Returns the string form of the arguments to the given instruction. + * The instruction must be of this instance's format. If the instruction + * has no arguments, then the result should be {@code ""}, not + * {@code null}. + * + * <p>Subclasses must override this method.</p> + * + * @param insn {@code non-null;} the instruction + * @return {@code non-null;} the string form + */ + public abstract String insnArgString(DalvInsn insn); + + /** + * Returns the associated comment for the given instruction, if any. + * The instruction must be of this instance's format. If the instruction + * has no comment, then the result should be {@code ""}, not + * {@code null}. + * + * <p>Subclasses must override this method.</p> + * + * @param insn {@code non-null;} the instruction + * @param noteIndices whether to include an explicit notation of + * constant pool indices + * @return {@code non-null;} the string form + */ + public abstract String insnCommentString(DalvInsn insn, boolean noteIndices); + + /** + * Gets the code size of instructions that use this format. The + * size is a number of 16-bit code units, not bytes. This should + * throw an exception if this format is of variable size. + * + * @return {@code >= 0;} the instruction length in 16-bit code units + */ + public abstract int codeSize(); + + /** + * Returns whether or not the given instruction's arguments will + * fit in this instance's format. This includes such things as + * counting register arguments, checking register ranges, and + * making sure that additional arguments are of appropriate types + * and are in-range. If this format has a branch target but the + * instruction's branch offset is unknown, this method will simply + * not check the offset. + * + * <p>Subclasses must override this method.</p> + * + * @param insn {@code non-null;} the instruction to check + * @return {@code true} iff the instruction's arguments are + * appropriate for this instance, or {@code false} if not + */ + public abstract boolean isCompatible(DalvInsn insn); + + /** + * Returns which of a given instruction's registers will fit in + * this instance's format. + * + * <p>The default implementation of this method always returns + * an empty BitSet. Subclasses must override this method if they + * have registers.</p> + * + * @param insn {@code non-null;} the instruction to check + * @return {@code non-null;} a BitSet flagging registers in the + * register list that are compatible to this format + */ + public BitSet compatibleRegs(DalvInsn insn) { + return new BitSet(); + } + + /** + * Returns whether or not the given instruction's branch offset will + * fit in this instance's format. This always returns {@code false} + * for formats that don't include a branch offset. + * + * <p>The default implementation of this method always returns + * {@code false}. Subclasses must override this method if they + * include branch offsets.</p> + * + * @param insn {@code non-null;} the instruction to check + * @return {@code true} iff the instruction's branch offset is + * appropriate for this instance, or {@code false} if not + */ + public boolean branchFits(TargetInsn insn) { + return false; + } + + /** + * Writes the code units for the given instruction to the given + * output destination. The instruction must be of this instance's format. + * + * <p>Subclasses must override this method.</p> + * + * @param out {@code non-null;} the output destination to write to + * @param insn {@code non-null;} the instruction to write + */ + public abstract void writeTo(AnnotatedOutput out, DalvInsn insn); + + /** + * Helper method to return a register list string. + * + * @param list {@code non-null;} the list of registers + * @return {@code non-null;} the string form + */ + protected static String regListString(RegisterSpecList list) { + int sz = list.size(); + StringBuffer sb = new StringBuffer(sz * 5 + 2); + + sb.append('{'); + + for (int i = 0; i < sz; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(list.get(i).regString()); + } + + sb.append('}'); + + return sb.toString(); + } + + /** + * Helper method to return a register range string. + * + * @param list {@code non-null;} the list of registers (which must be + * sequential) + * @return {@code non-null;} the string form + */ + protected static String regRangeString(RegisterSpecList list) { + int size = list.size(); + StringBuilder sb = new StringBuilder(30); + + sb.append("{"); + + switch (size) { + case 0: { + // Nothing to do. + break; + } + case 1: { + sb.append(list.get(0).regString()); + break; + } + default: { + RegisterSpec lastReg = list.get(size - 1); + if (lastReg.getCategory() == 2) { + /* + * Add one to properly represent a list-final + * category-2 register. + */ + lastReg = lastReg.withOffset(1); } - if (comment.length() != 0) { - sb.append(" // "); - sb.append(comment); - } - - return sb.toString(); - } - - /** - * Returns the string form of the arguments to the given instruction. - * The instruction must be of this instance's format. If the instruction - * has no arguments, then the result should be {@code ""}, not - * {@code null}. - * - * <p>Subclasses must override this method.</p> - * - * @param insn {@code non-null;} the instruction - * @return {@code non-null;} the string form - */ - public abstract String insnArgString(DalvInsn insn); - - /** - * Returns the associated comment for the given instruction, if any. - * The instruction must be of this instance's format. If the instruction - * has no comment, then the result should be {@code ""}, not - * {@code null}. - * - * <p>Subclasses must override this method.</p> - * - * @param insn {@code non-null;} the instruction - * @param noteIndices whether to include an explicit notation of - * constant pool indices - * @return {@code non-null;} the string form - */ - public abstract String insnCommentString(DalvInsn insn, - boolean noteIndices); - - /** - * Gets the code size of instructions that use this format. The - * size is a number of 16-bit code units, not bytes. This should - * throw an exception if this format is of variable size. - * - * @return {@code >= 0;} the instruction length in 16-bit code units - */ - public abstract int codeSize(); - - /** - * Returns whether or not the given instruction's arguments will - * fit in this instance's format. This includes such things as - * counting register arguments, checking register ranges, and - * making sure that additional arguments are of appropriate types - * and are in-range. If this format has a branch target but the - * instruction's branch offset is unknown, this method will simply - * not check the offset. - * - * <p>Subclasses must override this method.</p> - * - * @param insn {@code non-null;} the instruction to check - * @return {@code true} iff the instruction's arguments are - * appropriate for this instance, or {@code false} if not - */ - public abstract boolean isCompatible(DalvInsn insn); - - /** - * Returns which of a given instruction's registers will fit in - * this instance's format. - * - * <p>The default implementation of this method always returns - * an empty BitSet. Subclasses must override this method if they - * have registers.</p> - * - * @param insn {@code non-null;} the instruction to check - * @return {@code non-null;} a BitSet flagging registers in the - * register list that are compatible to this format - */ - public BitSet compatibleRegs(DalvInsn insn) { - return new BitSet(); - } - - /** - * Returns whether or not the given instruction's branch offset will - * fit in this instance's format. This always returns {@code false} - * for formats that don't include a branch offset. - * - * <p>The default implementation of this method always returns - * {@code false}. Subclasses must override this method if they - * include branch offsets.</p> - * - * @param insn {@code non-null;} the instruction to check - * @return {@code true} iff the instruction's branch offset is - * appropriate for this instance, or {@code false} if not - */ - public boolean branchFits(TargetInsn insn) { + sb.append(list.get(0).regString()); + sb.append(".."); + sb.append(lastReg.regString()); + } + } + + sb.append("}"); + + return sb.toString(); + } + + /** + * Helper method to return a literal bits argument string. + * + * @param value the value + * @return {@code non-null;} the string form + */ + protected static String literalBitsString(CstLiteralBits value) { + StringBuffer sb = new StringBuffer(100); + + sb.append('#'); + + if (value instanceof CstKnownNull) { + sb.append("null"); + } else { + sb.append(value.typeName()); + sb.append(' '); + sb.append(value.toHuman()); + } + + return sb.toString(); + } + + /** + * Helper method to return a literal bits comment string. + * + * @param value the value + * @param width the width of the constant, in bits (used for displaying + * the uninterpreted bits; one of: {@code 4 8 16 32 64} + * @return {@code non-null;} the comment + */ + protected static String literalBitsComment(CstLiteralBits value, int width) { + StringBuffer sb = new StringBuffer(20); + + sb.append("#"); + + long bits; + + if (value instanceof CstLiteral64) { + bits = ((CstLiteral64) value).getLongBits(); + } else { + bits = value.getIntBits(); + } + + switch (width) { + case 4: + sb.append(Hex.uNibble((int) bits)); + break; + case 8: + sb.append(Hex.u1((int) bits)); + break; + case 16: + sb.append(Hex.u2((int) bits)); + break; + case 32: + sb.append(Hex.u4((int) bits)); + break; + case 64: + sb.append(Hex.u8(bits)); + break; + default: { + throw new RuntimeException("shouldn't happen"); + } + } + + return sb.toString(); + } + + /** + * Helper method to return a branch address string. + * + * @param insn {@code non-null;} the instruction in question + * @return {@code non-null;} the string form of the instruction's + * branch target + */ + protected static String branchString(DalvInsn insn) { + TargetInsn ti = (TargetInsn) insn; + int address = ti.getTargetAddress(); + + return (address == (char) address) ? Hex.u2(address) : Hex.u4(address); + } + + /** + * Helper method to return the comment for a branch. + * + * @param insn {@code non-null;} the instruction in question + * @return {@code non-null;} the comment + */ + protected static String branchComment(DalvInsn insn) { + TargetInsn ti = (TargetInsn) insn; + int offset = ti.getTargetOffset(); + + return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset); + } + + /** + * Helper method to return the constant string for a {@link CstInsn} + * in human form. + * + * @param insn {@code non-null;} a constant-bearing instruction + * @return {@code non-null;} the human string form of the contained + * constant + */ + protected static String cstString(DalvInsn insn) { + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); + + return cst instanceof CstString ? ((CstString) cst).toQuoted() : cst.toHuman(); + } + + /** + * Helper method to return an instruction comment for a constant. + * + * @param insn {@code non-null;} a constant-bearing instruction + * @return {@code non-null;} comment string representing the constant + */ + protected static String cstComment(DalvInsn insn) { + CstInsn ci = (CstInsn) insn; + + if (!ci.hasIndex()) { + return ""; + } + + StringBuilder sb = new StringBuilder(20); + int index = ci.getIndex(); + + sb.append(ci.getConstant().typeName()); + sb.append('@'); + + if (index < 65536) { + sb.append(Hex.u2(index)); + } else { + sb.append(Hex.u4(index)); + } + + return sb.toString(); + } + + /** + * Helper method to determine if a signed int value fits in a nibble. + * + * @param value the value in question + * @return {@code true} iff it's in the range -8..+7 + */ + protected static boolean signedFitsInNibble(int value) { + return (value >= -8) && (value <= 7); + } + + /** + * Helper method to determine if an unsigned int value fits in a nibble. + * + * @param value the value in question + * @return {@code true} iff it's in the range 0..0xf + */ + protected static boolean unsignedFitsInNibble(int value) { + return value == (value & 0xf); + } + + /** + * Helper method to determine if a signed int value fits in a byte. + * + * @param value the value in question + * @return {@code true} iff it's in the range -0x80..+0x7f + */ + protected static boolean signedFitsInByte(int value) { + return (byte) value == value; + } + + /** + * Helper method to determine if an unsigned int value fits in a byte. + * + * @param value the value in question + * @return {@code true} iff it's in the range 0..0xff + */ + protected static boolean unsignedFitsInByte(int value) { + return value == (value & 0xff); + } + + /** + * Helper method to determine if a signed int value fits in a short. + * + * @param value the value in question + * @return {@code true} iff it's in the range -0x8000..+0x7fff + */ + protected static boolean signedFitsInShort(int value) { + return (short) value == value; + } + + /** + * Helper method to determine if an unsigned int value fits in a short. + * + * @param value the value in question + * @return {@code true} iff it's in the range 0..0xffff + */ + protected static boolean unsignedFitsInShort(int value) { + return value == (value & 0xffff); + } + + /** + * Helper method to determine if a list of registers are sequential, + * including degenerate cases for empty or single-element lists. + * + * @param list {@code non-null;} the list of registers + * @return {@code true} iff the list is sequentially ordered + */ + protected static boolean isRegListSequential(RegisterSpecList list) { + int sz = list.size(); + + if (sz < 2) { + return true; + } + + int first = list.get(0).getReg(); + int next = first; + + for (int i = 0; i < sz; i++) { + RegisterSpec one = list.get(i); + if (one.getReg() != next) { return false; + } + next += one.getCategory(); } - /** - * Writes the code units for the given instruction to the given - * output destination. The instruction must be of this instance's format. - * - * <p>Subclasses must override this method.</p> - * - * @param out {@code non-null;} the output destination to write to - * @param insn {@code non-null;} the instruction to write - */ - public abstract void writeTo(AnnotatedOutput out, DalvInsn insn); - - /** - * Helper method to return a register list string. - * - * @param list {@code non-null;} the list of registers - * @return {@code non-null;} the string form - */ - protected static String regListString(RegisterSpecList list) { - int sz = list.size(); - StringBuffer sb = new StringBuffer(sz * 5 + 2); - - sb.append('{'); - - for (int i = 0; i < sz; i++) { - if (i != 0) { - sb.append(", "); - } - sb.append(list.get(i).regString()); - } - - sb.append('}'); - - return sb.toString(); - } - - /** - * Helper method to return a register range string. - * - * @param list {@code non-null;} the list of registers (which must be - * sequential) - * @return {@code non-null;} the string form - */ - protected static String regRangeString(RegisterSpecList list) { - int size = list.size(); - StringBuilder sb = new StringBuilder(30); - - sb.append("{"); - - switch (size) { - case 0: { - // Nothing to do. - break; - } - case 1: { - sb.append(list.get(0).regString()); - break; - } - default: { - RegisterSpec lastReg = list.get(size - 1); - if (lastReg.getCategory() == 2) { - /* - * Add one to properly represent a list-final - * category-2 register. - */ - lastReg = lastReg.withOffset(1); - } - - sb.append(list.get(0).regString()); - sb.append(".."); - sb.append(lastReg.regString()); - } - } - - sb.append("}"); - - return sb.toString(); - } - - /** - * Helper method to return a literal bits argument string. - * - * @param value the value - * @return {@code non-null;} the string form - */ - protected static String literalBitsString(CstLiteralBits value) { - StringBuffer sb = new StringBuffer(100); - - sb.append('#'); - - if (value instanceof CstKnownNull) { - sb.append("null"); - } else { - sb.append(value.typeName()); - sb.append(' '); - sb.append(value.toHuman()); - } - - return sb.toString(); - } - - /** - * Helper method to return a literal bits comment string. - * - * @param value the value - * @param width the width of the constant, in bits (used for displaying - * the uninterpreted bits; one of: {@code 4 8 16 32 64} - * @return {@code non-null;} the comment - */ - protected static String literalBitsComment(CstLiteralBits value, - int width) { - StringBuffer sb = new StringBuffer(20); - - sb.append("#"); - - long bits; - - if (value instanceof CstLiteral64) { - bits = ((CstLiteral64) value).getLongBits(); - } else { - bits = value.getIntBits(); - } - - switch (width) { - case 4: sb.append(Hex.uNibble((int) bits)); break; - case 8: sb.append(Hex.u1((int) bits)); break; - case 16: sb.append(Hex.u2((int) bits)); break; - case 32: sb.append(Hex.u4((int) bits)); break; - case 64: sb.append(Hex.u8(bits)); break; - default: { - throw new RuntimeException("shouldn't happen"); - } - } - - return sb.toString(); - } - - /** - * Helper method to return a branch address string. - * - * @param insn {@code non-null;} the instruction in question - * @return {@code non-null;} the string form of the instruction's - * branch target - */ - protected static String branchString(DalvInsn insn) { - TargetInsn ti = (TargetInsn) insn; - int address = ti.getTargetAddress(); - - return (address == (char) address) ? Hex.u2(address) : Hex.u4(address); - } - - /** - * Helper method to return the comment for a branch. - * - * @param insn {@code non-null;} the instruction in question - * @return {@code non-null;} the comment - */ - protected static String branchComment(DalvInsn insn) { - TargetInsn ti = (TargetInsn) insn; - int offset = ti.getTargetOffset(); - - return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset); - } - - /** - * Helper method to return the constant string for a {@link CstInsn} - * in human form. - * - * @param insn {@code non-null;} a constant-bearing instruction - * @return {@code non-null;} the human string form of the contained - * constant - */ - protected static String cstString(DalvInsn insn) { - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); - - return cst instanceof CstString ? ((CstString) cst).toQuoted() : cst.toHuman(); - } - - /** - * Helper method to return an instruction comment for a constant. - * - * @param insn {@code non-null;} a constant-bearing instruction - * @return {@code non-null;} comment string representing the constant - */ - protected static String cstComment(DalvInsn insn) { - CstInsn ci = (CstInsn) insn; - - if (! ci.hasIndex()) { - return ""; - } - - StringBuilder sb = new StringBuilder(20); - int index = ci.getIndex(); - - sb.append(ci.getConstant().typeName()); - sb.append('@'); - - if (index < 65536) { - sb.append(Hex.u2(index)); - } else { - sb.append(Hex.u4(index)); - } - - return sb.toString(); - } - - /** - * Helper method to determine if a signed int value fits in a nibble. - * - * @param value the value in question - * @return {@code true} iff it's in the range -8..+7 - */ - protected static boolean signedFitsInNibble(int value) { - return (value >= -8) && (value <= 7); - } - - /** - * Helper method to determine if an unsigned int value fits in a nibble. - * - * @param value the value in question - * @return {@code true} iff it's in the range 0..0xf - */ - protected static boolean unsignedFitsInNibble(int value) { - return value == (value & 0xf); - } - - /** - * Helper method to determine if a signed int value fits in a byte. - * - * @param value the value in question - * @return {@code true} iff it's in the range -0x80..+0x7f - */ - protected static boolean signedFitsInByte(int value) { - return (byte) value == value; - } - - /** - * Helper method to determine if an unsigned int value fits in a byte. - * - * @param value the value in question - * @return {@code true} iff it's in the range 0..0xff - */ - protected static boolean unsignedFitsInByte(int value) { - return value == (value & 0xff); - } - - /** - * Helper method to determine if a signed int value fits in a short. - * - * @param value the value in question - * @return {@code true} iff it's in the range -0x8000..+0x7fff - */ - protected static boolean signedFitsInShort(int value) { - return (short) value == value; - } - - /** - * Helper method to determine if an unsigned int value fits in a short. - * - * @param value the value in question - * @return {@code true} iff it's in the range 0..0xffff - */ - protected static boolean unsignedFitsInShort(int value) { - return value == (value & 0xffff); - } - - /** - * Helper method to determine if a list of registers are sequential, - * including degenerate cases for empty or single-element lists. - * - * @param list {@code non-null;} the list of registers - * @return {@code true} iff the list is sequentially ordered - */ - protected static boolean isRegListSequential(RegisterSpecList list) { - int sz = list.size(); - - if (sz < 2) { - return true; - } - - int first = list.get(0).getReg(); - int next = first; - - for (int i = 0; i < sz; i++) { - RegisterSpec one = list.get(i); - if (one.getReg() != next) { - return false; - } - next += one.getCategory(); - } - - return true; - } - - /** - * Helper method to extract the callout-argument index from an - * appropriate instruction. - * - * @param insn {@code non-null;} the instruction - * @return {@code >= 0;} the callout argument index - */ - protected static int argIndex(DalvInsn insn) { - int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue(); - - if (arg < 0) { - throw new IllegalArgumentException("bogus insn"); - } - - return arg; - } - - /** - * Helper method to combine an opcode and a second byte of data into - * the appropriate form for emitting into a code buffer. - * - * @param insn {@code non-null;} the instruction containing the opcode - * @param arg {@code 0..255;} arbitrary other byte value - * @return combined value - */ - protected static short opcodeUnit(DalvInsn insn, int arg) { - if ((arg & 0xff) != arg) { - throw new IllegalArgumentException("arg out of range 0..255"); - } - - int opcode = insn.getOpcode().getOpcode(); + return true; + } - if ((opcode & 0xff) != opcode) { - throw new IllegalArgumentException("opcode out of range 0..255"); - } + /** + * Helper method to extract the callout-argument index from an + * appropriate instruction. + * + * @param insn {@code non-null;} the instruction + * @return {@code >= 0;} the callout argument index + */ + protected static int argIndex(DalvInsn insn) { + int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue(); - return (short) (opcode | (arg << 8)); + if (arg < 0) { + throw new IllegalArgumentException("bogus insn"); } - /** - * Helper method to get an extended (16-bit) opcode out of an - * instruction, returning it as a code unit. The opcode - * <i>must</i> be an extended opcode. - * - * @param insn {@code non-null;} the instruction containing the - * extended opcode - * @return the opcode as a code unit - */ - protected static short opcodeUnit(DalvInsn insn) { - int opcode = insn.getOpcode().getOpcode(); - - if ((opcode < 0x100) || (opcode > 0xffff)) { - throw new IllegalArgumentException("opcode out of range 0..65535"); - } + return arg; + } - return (short) opcode; + /** + * Helper method to combine an opcode and a second byte of data into + * the appropriate form for emitting into a code buffer. + * + * @param insn {@code non-null;} the instruction containing the opcode + * @param arg {@code 0..255;} arbitrary other byte value + * @return combined value + */ + protected static short opcodeUnit(DalvInsn insn, int arg) { + if ((arg & 0xff) != arg) { + throw new IllegalArgumentException("arg out of range 0..255"); } - /** - * Helper method to combine two bytes into a code unit. - * - * @param low {@code 0..255;} low byte - * @param high {@code 0..255;} high byte - * @return combined value - */ - protected static short codeUnit(int low, int high) { - if ((low & 0xff) != low) { - throw new IllegalArgumentException("low out of range 0..255"); - } + int opcode = insn.getOpcode().getOpcode(); - if ((high & 0xff) != high) { - throw new IllegalArgumentException("high out of range 0..255"); - } - - return (short) (low | (high << 8)); + if ((opcode & 0xff) != opcode) { + throw new IllegalArgumentException("opcode out of range 0..255"); } - /** - * Helper method to combine four nibbles into a code unit. - * - * @param n0 {@code 0..15;} low nibble - * @param n1 {@code 0..15;} medium-low nibble - * @param n2 {@code 0..15;} medium-high nibble - * @param n3 {@code 0..15;} high nibble - * @return combined value - */ - protected static short codeUnit(int n0, int n1, int n2, int n3) { - if ((n0 & 0xf) != n0) { - throw new IllegalArgumentException("n0 out of range 0..15"); - } - - if ((n1 & 0xf) != n1) { - throw new IllegalArgumentException("n1 out of range 0..15"); - } - - if ((n2 & 0xf) != n2) { - throw new IllegalArgumentException("n2 out of range 0..15"); - } - - if ((n3 & 0xf) != n3) { - throw new IllegalArgumentException("n3 out of range 0..15"); - } + return (short) (opcode | (arg << 8)); + } - return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12)); - } - - /** - * Helper method to combine two nibbles into a byte. - * - * @param low {@code 0..15;} low nibble - * @param high {@code 0..15;} high nibble - * @return {@code 0..255;} combined value - */ - protected static int makeByte(int low, int high) { - if ((low & 0xf) != low) { - throw new IllegalArgumentException("low out of range 0..15"); - } - - if ((high & 0xf) != high) { - throw new IllegalArgumentException("high out of range 0..15"); - } - - return low | (high << 4); - } - - /** - * Writes one code unit to the given output destination. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - */ - protected static void write(AnnotatedOutput out, short c0) { - out.writeShort(c0); - } - - /** - * Writes two code units to the given output destination. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - * @param c1 code unit to write - */ - protected static void write(AnnotatedOutput out, short c0, short c1) { - out.writeShort(c0); - out.writeShort(c1); - } - - /** - * Writes three code units to the given output destination. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - * @param c1 code unit to write - * @param c2 code unit to write - */ - protected static void write(AnnotatedOutput out, short c0, short c1, - short c2) { - out.writeShort(c0); - out.writeShort(c1); - out.writeShort(c2); - } - - /** - * Writes four code units to the given output destination. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - * @param c1 code unit to write - * @param c2 code unit to write - * @param c3 code unit to write - */ - protected static void write(AnnotatedOutput out, short c0, short c1, - short c2, short c3) { - out.writeShort(c0); - out.writeShort(c1); - out.writeShort(c2); - out.writeShort(c3); - } - - /** - * Writes five code units to the given output destination. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - * @param c1 code unit to write - * @param c2 code unit to write - * @param c3 code unit to write - * @param c4 code unit to write - */ - protected static void write(AnnotatedOutput out, short c0, short c1, - short c2, short c3, short c4) { - out.writeShort(c0); - out.writeShort(c1); - out.writeShort(c2); - out.writeShort(c3); - out.writeShort(c4); - } - - /** - * Writes three code units to the given output destination, where the - * second and third are represented as single <code>int</code> and emitted - * in little-endian order. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - * @param c1c2 code unit pair to write - */ - protected static void write(AnnotatedOutput out, short c0, int c1c2) { - write(out, c0, (short) c1c2, (short) (c1c2 >> 16)); - } - - /** - * Writes four code units to the given output destination, where the - * second and third are represented as single <code>int</code> and emitted - * in little-endian order. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - * @param c1c2 code unit pair to write - * @param c3 code unit to write - */ - protected static void write(AnnotatedOutput out, short c0, int c1c2, - short c3) { - write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3); - } - - /** - * Writes five code units to the given output destination, where the - * second and third are represented as single <code>int</code> and emitted - * in little-endian order. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - * @param c1c2 code unit pair to write - * @param c3 code unit to write - * @param c4 code unit to write - */ - protected static void write(AnnotatedOutput out, short c0, int c1c2, - short c3, short c4) { - write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4); - } - - /** - * Writes five code units to the given output destination, where the - * second through fifth are represented as single <code>long</code> - * and emitted in little-endian order. - * - * @param out {@code non-null;} where to write to - * @param c0 code unit to write - * @param c1c2c3c4 code unit quad to write - */ - protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) { - write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16), - (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48)); - } + /** + * Helper method to get an extended (16-bit) opcode out of an + * instruction, returning it as a code unit. The opcode + * <i>must</i> be an extended opcode. + * + * @param insn {@code non-null;} the instruction containing the + * extended opcode + * @return the opcode as a code unit + */ + protected static short opcodeUnit(DalvInsn insn) { + int opcode = insn.getOpcode().getOpcode(); + + if ((opcode < 0x100) || (opcode > 0xffff)) { + throw new IllegalArgumentException("opcode out of range 0..65535"); + } + + return (short) opcode; + } + + /** + * Helper method to combine two bytes into a code unit. + * + * @param low {@code 0..255;} low byte + * @param high {@code 0..255;} high byte + * @return combined value + */ + protected static short codeUnit(int low, int high) { + if ((low & 0xff) != low) { + throw new IllegalArgumentException("low out of range 0..255"); + } + + if ((high & 0xff) != high) { + throw new IllegalArgumentException("high out of range 0..255"); + } + + return (short) (low | (high << 8)); + } + + /** + * Helper method to combine four nibbles into a code unit. + * + * @param n0 {@code 0..15;} low nibble + * @param n1 {@code 0..15;} medium-low nibble + * @param n2 {@code 0..15;} medium-high nibble + * @param n3 {@code 0..15;} high nibble + * @return combined value + */ + protected static short codeUnit(int n0, int n1, int n2, int n3) { + if ((n0 & 0xf) != n0) { + throw new IllegalArgumentException("n0 out of range 0..15"); + } + + if ((n1 & 0xf) != n1) { + throw new IllegalArgumentException("n1 out of range 0..15"); + } + + if ((n2 & 0xf) != n2) { + throw new IllegalArgumentException("n2 out of range 0..15"); + } + + if ((n3 & 0xf) != n3) { + throw new IllegalArgumentException("n3 out of range 0..15"); + } + + return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12)); + } + + /** + * Helper method to combine two nibbles into a byte. + * + * @param low {@code 0..15;} low nibble + * @param high {@code 0..15;} high nibble + * @return {@code 0..255;} combined value + */ + protected static int makeByte(int low, int high) { + if ((low & 0xf) != low) { + throw new IllegalArgumentException("low out of range 0..15"); + } + + if ((high & 0xf) != high) { + throw new IllegalArgumentException("high out of range 0..15"); + } + + return low | (high << 4); + } + + /** + * Writes one code unit to the given output destination. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + */ + protected static void write(AnnotatedOutput out, short c0) { + out.writeShort(c0); + } + + /** + * Writes two code units to the given output destination. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + * @param c1 code unit to write + */ + protected static void write(AnnotatedOutput out, short c0, short c1) { + out.writeShort(c0); + out.writeShort(c1); + } + + /** + * Writes three code units to the given output destination. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + * @param c1 code unit to write + * @param c2 code unit to write + */ + protected static void write(AnnotatedOutput out, short c0, short c1, short c2) { + out.writeShort(c0); + out.writeShort(c1); + out.writeShort(c2); + } + + /** + * Writes four code units to the given output destination. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + * @param c1 code unit to write + * @param c2 code unit to write + * @param c3 code unit to write + */ + protected static void write(AnnotatedOutput out, short c0, short c1, short c2, short c3) { + out.writeShort(c0); + out.writeShort(c1); + out.writeShort(c2); + out.writeShort(c3); + } + + /** + * Writes five code units to the given output destination. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + * @param c1 code unit to write + * @param c2 code unit to write + * @param c3 code unit to write + * @param c4 code unit to write + */ + protected static void write(AnnotatedOutput out, + short c0, + short c1, + short c2, + short c3, + short c4) { + out.writeShort(c0); + out.writeShort(c1); + out.writeShort(c2); + out.writeShort(c3); + out.writeShort(c4); + } + + /** + * Writes three code units to the given output destination, where the + * second and third are represented as single <code>int</code> and emitted + * in little-endian order. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + * @param c1c2 code unit pair to write + */ + protected static void write(AnnotatedOutput out, short c0, int c1c2) { + write(out, c0, (short) c1c2, (short) (c1c2 >> 16)); + } + + /** + * Writes four code units to the given output destination, where the + * second and third are represented as single <code>int</code> and emitted + * in little-endian order. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + * @param c1c2 code unit pair to write + * @param c3 code unit to write + */ + protected static void write(AnnotatedOutput out, short c0, int c1c2, short c3) { + write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3); + } + + /** + * Writes five code units to the given output destination, where the + * second and third are represented as single <code>int</code> and emitted + * in little-endian order. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + * @param c1c2 code unit pair to write + * @param c3 code unit to write + * @param c4 code unit to write + */ + protected static void write(AnnotatedOutput out, short c0, int c1c2, short c3, short c4) { + write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4); + } + + /** + * Writes five code units to the given output destination, where the + * second through fifth are represented as single <code>long</code> + * and emitted in little-endian order. + * + * @param out {@code non-null;} where to write to + * @param c0 code unit to write + * @param c1c2c3c4 code unit quad to write + */ + protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) { + write(out, + c0, + (short) c1c2c3c4, + (short) (c1c2c3c4 >> 16), + (short) (c1c2c3c4 >> 32), + (short) (c1c2c3c4 >> 48)); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/LocalEnd.java b/dx/src/com/android/jack/dx/dex/code/LocalEnd.java index a72044a..897ef7c 100644 --- a/dx/src/com/android/jack/dx/dex/code/LocalEnd.java +++ b/dx/src/com/android/jack/dx/dex/code/LocalEnd.java @@ -27,64 +27,64 @@ import com.android.jack.dx.rop.code.SourcePosition; * subsequent instruction, the indicated variable is no longer valid. */ public final class LocalEnd extends ZeroSizeInsn { - /** - * {@code non-null;} register spec representing the local variable ended - * by this instance. <b>Note:</b> Technically, only the register - * number needs to be recorded here as the rest of the information - * is implicit in the ambient local variable state, but other code - * will check the other info for consistency. - */ - private final RegisterSpec local; + /** + * {@code non-null;} register spec representing the local variable ended + * by this instance. <b>Note:</b> Technically, only the register + * number needs to be recorded here as the rest of the information + * is implicit in the ambient local variable state, but other code + * will check the other info for consistency. + */ + private final RegisterSpec local; - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - * @param local {@code non-null;} register spec representing the local - * variable introduced by this instance - */ - public LocalEnd(SourcePosition position, RegisterSpec local) { - super(position); + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + * @param local {@code non-null;} register spec representing the local + * variable introduced by this instance + */ + public LocalEnd(SourcePosition position, RegisterSpec local) { + super(position); - if (local == null) { - throw new NullPointerException("local == null"); - } - - this.local = local; + if (local == null) { + throw new NullPointerException("local == null"); } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisterOffset(int delta) { - return new LocalEnd(getPosition(), local.withOffset(delta)); - } + this.local = local; + } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new LocalEnd(getPosition(), local); - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisterOffset(int delta) { + return new LocalEnd(getPosition(), local.withOffset(delta)); + } - /** - * Gets the register spec representing the local variable ended - * by this instance. - * - * @return {@code non-null;} the register spec - */ - public RegisterSpec getLocal() { - return local; - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new LocalEnd(getPosition(), local); + } - /** {@inheritDoc} */ - @Override - protected String argString() { - return local.toString(); - } + /** + * Gets the register spec representing the local variable ended + * by this instance. + * + * @return {@code non-null;} the register spec + */ + public RegisterSpec getLocal() { + return local; + } - /** {@inheritDoc} */ - @Override - protected String listingString0(boolean noteIndices) { - return "local-end " + LocalStart.localString(local); - } + /** {@inheritDoc} */ + @Override + protected String argString() { + return local.toString(); + } + + /** {@inheritDoc} */ + @Override + protected String listingString0(boolean noteIndices) { + return "local-end " + LocalStart.localString(local); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/LocalList.java b/dx/src/com/android/jack/dx/dex/code/LocalList.java index 0a07a60..8f1c713 100644 --- a/dx/src/com/android/jack/dx/dex/code/LocalList.java +++ b/dx/src/com/android/jack/dx/dex/code/LocalList.java @@ -33,916 +33,895 @@ import java.util.Arrays; * and a type. */ public final class LocalList extends FixedSizeList { - /** {@code non-null;} empty instance */ - public static final LocalList EMPTY = new LocalList(0); + /** {@code non-null;} empty instance */ + public static final LocalList EMPTY = new LocalList(0); + + /** whether to run the self-check code */ + private static final boolean DEBUG = false; + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size {@code >= 0;} the size of the list + */ + public LocalList(int size) { + super(size); + } + + /** + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index + */ + public Entry get(int n) { + return (Entry) get0(n); + } + + /** + * Sets the entry at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param entry {@code non-null;} the entry to set at {@code n} + */ + public void set(int n, Entry entry) { + set0(n, entry); + } + + /** + * Does a human-friendly dump of this instance. + * + * @param out {@code non-null;} where to dump + * @param prefix {@code non-null;} prefix to attach to each line of output + */ + public void debugPrint(PrintStream out, String prefix) { + int sz = size(); + + for (int i = 0; i < sz; i++) { + out.print(prefix); + out.println(get(i)); + } + } + + /** + * Disposition of a local entry. + */ + public static enum Disposition { + /** local started (introduced) */ + START, + + /** local ended without being replaced */ + END_SIMPLY, + + /** local ended because it was directly replaced */ + END_REPLACED, - /** whether to run the self-check code */ - private static final boolean DEBUG = false; + /** local ended because it was moved to a different register */ + END_MOVED, /** - * Constructs an instance. All indices initially contain {@code null}. + * local ended because the previous local clobbered this one + * (because it is category-2) + */ + END_CLOBBERED_BY_PREV, + + /** + * local ended because the next local clobbered this one + * (because this one is a category-2) + */ + END_CLOBBERED_BY_NEXT; + } + + /** + * Entry in a local list. + */ + public static class Entry implements Comparable<Entry> { + /** {@code >= 0;} address */ + private final int address; + + /** {@code non-null;} disposition of the local */ + private final Disposition disposition; + + /** {@code non-null;} register spec representing the variable */ + private final RegisterSpec spec; + + /** {@code non-null;} variable type (derived from {@code spec}) */ + private final CstType type; + + /** + * Constructs an instance. * - * @param size {@code >= 0;} the size of the list + * @param address {@code >= 0;} address + * @param disposition {@code non-null;} disposition of the local + * @param spec {@code non-null;} register spec representing + * the variable */ - public LocalList(int size) { - super(size); + public Entry(int address, Disposition disposition, RegisterSpec spec) { + if (address < 0) { + throw new IllegalArgumentException("address < 0"); + } + + if (disposition == null) { + throw new NullPointerException("disposition == null"); + } + + try { + if (spec.getLocalItem() == null) { + throw new NullPointerException("spec.getLocalItem() == null"); + } + } catch (NullPointerException ex) { + // Elucidate the exception. + throw new NullPointerException("spec == null"); + } + + this.address = address; + this.disposition = disposition; + this.spec = spec; + this.type = spec.getLocalItem().getType(); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return Integer.toHexString(address) + " " + disposition + " " + spec; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof Entry)) { + return false; + } + + return (compareTo((Entry) other) == 0); } /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. + * Compares by (in priority order) address, end then start + * disposition (variants of end are all consistered + * equivalent), and spec. * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index + * @param other {@code non-null;} entry to compare to + * @return {@code -1..1;} standard result of comparison */ - public Entry get(int n) { - return (Entry) get0(n); + @Override + public int compareTo(Entry other) { + if (address < other.address) { + return -1; + } else if (address > other.address) { + return 1; + } + + boolean thisIsStart = isStart(); + boolean otherIsStart = other.isStart(); + + if (thisIsStart != otherIsStart) { + return thisIsStart ? 1 : -1; + } + + return spec.compareTo(other.spec); } /** - * Sets the entry at the given index. + * Gets the address. * - * @param n {@code >= 0, < size();} which index - * @param entry {@code non-null;} the entry to set at {@code n} + * @return {@code >= 0;} the address */ - public void set(int n, Entry entry) { - set0(n, entry); + public int getAddress() { + return address; } /** - * Does a human-friendly dump of this instance. + * Gets the disposition. * - * @param out {@code non-null;} where to dump - * @param prefix {@code non-null;} prefix to attach to each line of output + * @return {@code non-null;} the disposition */ - public void debugPrint(PrintStream out, String prefix) { - int sz = size(); - - for (int i = 0; i < sz; i++) { - out.print(prefix); - out.println(get(i)); - } + public Disposition getDisposition() { + return disposition; } /** - * Disposition of a local entry. + * Gets whether this is a local start. This is just shorthand for + * {@code getDisposition() == Disposition.START}. + * + * @return {@code true} iff this is a start */ - public static enum Disposition { - /** local started (introduced) */ - START, + public boolean isStart() { + return disposition == Disposition.START; + } - /** local ended without being replaced */ - END_SIMPLY, + /** + * Gets the variable name. + * + * @return {@code null-ok;} the variable name + */ + public CstString getName() { + return spec.getLocalItem().getName(); + } - /** local ended because it was directly replaced */ - END_REPLACED, + /** + * Gets the variable signature. + * + * @return {@code null-ok;} the variable signature + */ + public CstString getSignature() { + return spec.getLocalItem().getSignature(); + } - /** local ended because it was moved to a different register */ - END_MOVED, + /** + * Gets the variable's type. + * + * @return {@code non-null;} the type + */ + public CstType getType() { + return type; + } - /** - * local ended because the previous local clobbered this one - * (because it is category-2) - */ - END_CLOBBERED_BY_PREV, + /** + * Gets the number of the register holding the variable. + * + * @return {@code >= 0;} the number of the register holding + * the variable + */ + public int getRegister() { + return spec.getReg(); + } - /** - * local ended because the next local clobbered this one - * (because this one is a category-2) - */ - END_CLOBBERED_BY_NEXT; + /** + * Gets the RegisterSpec of the register holding the variable. + * + * @return {@code non-null;} RegisterSpec of the holding register. + */ + public RegisterSpec getRegisterSpec() { + return spec; } /** - * Entry in a local list. + * Returns whether or not this instance matches the given spec. + * + * @param otherSpec {@code non-null;} the spec in question + * @return {@code true} iff this instance matches + * {@code spec} */ - public static class Entry implements Comparable<Entry> { - /** {@code >= 0;} address */ - private final int address; - - /** {@code non-null;} disposition of the local */ - private final Disposition disposition; - - /** {@code non-null;} register spec representing the variable */ - private final RegisterSpec spec; - - /** {@code non-null;} variable type (derived from {@code spec}) */ - private final CstType type; - - /** - * Constructs an instance. - * - * @param address {@code >= 0;} address - * @param disposition {@code non-null;} disposition of the local - * @param spec {@code non-null;} register spec representing - * the variable - */ - public Entry(int address, Disposition disposition, RegisterSpec spec) { - if (address < 0) { - throw new IllegalArgumentException("address < 0"); - } + public boolean matches(RegisterSpec otherSpec) { + return spec.equalsUsingSimpleType(otherSpec); + } - if (disposition == null) { - throw new NullPointerException("disposition == null"); - } + /** + * Returns whether or not this instance matches the spec in + * the given instance. + * + * @param other {@code non-null;} another entry + * @return {@code true} iff this instance's spec matches + * {@code other} + */ + public boolean matches(Entry other) { + return matches(other.spec); + } - try { - if (spec.getLocalItem() == null) { - throw new NullPointerException( - "spec.getLocalItem() == null"); - } - } catch (NullPointerException ex) { - // Elucidate the exception. - throw new NullPointerException("spec == null"); - } + /** + * Returns an instance just like this one but with the disposition + * set as given. + * + * @param disposition {@code non-null;} the new disposition + * @return {@code non-null;} an appropriately-constructed instance + */ + public Entry withDisposition(Disposition disposition) { + if (disposition == this.disposition) { + return this; + } - this.address = address; - this.disposition = disposition; - this.spec = spec; - this.type = spec.getLocalItem().getType(); - } + return new Entry(address, disposition, spec); + } + } + + /** + * Constructs an instance for the given method, based on the given + * block order and intermediate local information. + * + * @param insns {@code non-null;} instructions to convert + * @return {@code non-null;} the constructed list + */ + public static LocalList make(DalvInsnList insns) { + int sz = insns.size(); + + /* + * Go through the insn list, looking for all the local + * variable pseudoinstructions, splitting out LocalSnapshots + * into separate per-variable starts, adding explicit ends + * wherever a variable is replaced or moved, and collecting + * these and all the other local variable "activity" + * together into an output list (without the other insns). + * + * Note: As of this writing, this method won't be handed any + * insn lists that contain local ends, but I (danfuzz) expect + * that to change at some point, when we start feeding that + * info explicitly into the rop layer rather than only trying + * to infer it. So, given that expectation, this code is + * written to deal with them. + */ - /** {@inheritDoc} */ - public String toString() { - return Integer.toHexString(address) + " " + disposition + " " + - spec; - } +MakeState state = new MakeState(sz); + + for (int i = 0; i < sz; i++) { + DalvInsn insn = insns.get(i); + + if (insn instanceof LocalSnapshot) { + RegisterSpecSet snapshot = ((LocalSnapshot) insn).getLocals(); + state.snapshot(insn.getAddress(), snapshot); + } else if (insn instanceof LocalStart) { + RegisterSpec local = ((LocalStart) insn).getLocal(); + state.startLocal(insn.getAddress(), local); + } else if (insn instanceof LocalEnd) { + RegisterSpec local = ((LocalEnd) insn).getLocal(); + state.endLocal(insn.getAddress(), local); + } + } - /** {@inheritDoc} */ - public boolean equals(Object other) { - if (!(other instanceof Entry)) { - return false; - } + LocalList result = state.finish(); - return (compareTo((Entry) other) == 0); - } + if (DEBUG) { + debugVerify(result); + } - /** - * Compares by (in priority order) address, end then start - * disposition (variants of end are all consistered - * equivalent), and spec. - * - * @param other {@code non-null;} entry to compare to - * @return {@code -1..1;} standard result of comparison - */ - public int compareTo(Entry other) { - if (address < other.address) { - return -1; - } else if (address > other.address) { - return 1; - } + return result; + } + + /** + * Debugging helper that verifies the constraint that a list doesn't + * contain any redundant local starts and that local ends that are + * due to replacements are properly annotated. + */ + private static void debugVerify(LocalList locals) { + try { + debugVerify0(locals); + } catch (RuntimeException ex) { + int sz = locals.size(); + for (int i = 0; i < sz; i++) { + System.err.println(locals.get(i)); + } + throw ex; + } - boolean thisIsStart = isStart(); - boolean otherIsStart = other.isStart(); + } - if (thisIsStart != otherIsStart) { - return thisIsStart ? 1 : -1; - } + /** + * Helper for {@link #debugVerify} which does most of the work. + */ + private static void debugVerify0(LocalList locals) { + int sz = locals.size(); + Entry[] active = new Entry[65536]; - return spec.compareTo(other.spec); - } + for (int i = 0; i < sz; i++) { + Entry e = locals.get(i); + int reg = e.getRegister(); - /** - * Gets the address. - * - * @return {@code >= 0;} the address - */ - public int getAddress() { - return address; - } + if (e.isStart()) { + Entry already = active[reg]; - /** - * Gets the disposition. - * - * @return {@code non-null;} the disposition - */ - public Disposition getDisposition() { - return disposition; + if ((already != null) && e.matches(already)) { + throw new RuntimeException("redundant start at " + Integer.toHexString(e.getAddress()) + + ": got " + e + "; had " + already); } - /** - * Gets whether this is a local start. This is just shorthand for - * {@code getDisposition() == Disposition.START}. - * - * @return {@code true} iff this is a start - */ - public boolean isStart() { - return disposition == Disposition.START; + active[reg] = e; + } else { + if (active[reg] == null) { + throw new RuntimeException("redundant end at " + Integer.toHexString(e.getAddress())); } - /** - * Gets the variable name. - * - * @return {@code null-ok;} the variable name - */ - public CstString getName() { - return spec.getLocalItem().getName(); + int addr = e.getAddress(); + boolean foundStart = false; + + for (int j = i + 1; j < sz; j++) { + Entry test = locals.get(j); + if (test.getAddress() != addr) { + break; + } + if (test.getRegisterSpec().getReg() == reg) { + if (test.isStart()) { + if (e.getDisposition() != Disposition.END_REPLACED) { + throw new RuntimeException("improperly marked end at " + Integer.toHexString(addr)); + } + foundStart = true; + } else { + throw new RuntimeException("redundant end at " + Integer.toHexString(addr)); + } + } } - /** - * Gets the variable signature. - * - * @return {@code null-ok;} the variable signature - */ - public CstString getSignature() { - return spec.getLocalItem().getSignature(); + if (!foundStart && (e.getDisposition() == Disposition.END_REPLACED)) { + throw new RuntimeException( + "improper end replacement claim at " + Integer.toHexString(addr)); } - /** - * Gets the variable's type. - * - * @return {@code non-null;} the type - */ - public CstType getType() { - return type; - } + active[reg] = null; + } + } + } - /** - * Gets the number of the register holding the variable. - * - * @return {@code >= 0;} the number of the register holding - * the variable - */ - public int getRegister() { - return spec.getReg(); - } + /** + * Intermediate state when constructing a local list. + */ + public static class MakeState { + /** {@code non-null;} result being collected */ + private final ArrayList<Entry> result; - /** - * Gets the RegisterSpec of the register holding the variable. - * - * @return {@code non-null;} RegisterSpec of the holding register. - */ - public RegisterSpec getRegisterSpec() { - return spec; - } + /** + * {@code >= 0;} running count of nulled result entries, to help with + * sizing the final list + */ + private int nullResultCount; - /** - * Returns whether or not this instance matches the given spec. - * - * @param otherSpec {@code non-null;} the spec in question - * @return {@code true} iff this instance matches - * {@code spec} - */ - public boolean matches(RegisterSpec otherSpec) { - return spec.equalsUsingSimpleType(otherSpec); - } + /** {@code null-ok;} current register mappings */ + private RegisterSpecSet regs; - /** - * Returns whether or not this instance matches the spec in - * the given instance. - * - * @param other {@code non-null;} another entry - * @return {@code true} iff this instance's spec matches - * {@code other} - */ - public boolean matches(Entry other) { - return matches(other.spec); - } + /** {@code null-ok;} result indices where local ends are stored */ + private int[] endIndices; - /** - * Returns an instance just like this one but with the disposition - * set as given. - * - * @param disposition {@code non-null;} the new disposition - * @return {@code non-null;} an appropriately-constructed instance - */ - public Entry withDisposition(Disposition disposition) { - if (disposition == this.disposition) { - return this; - } + /** {@code >= 0;} last address seen */ + private int lastAddress; - return new Entry(address, disposition, spec); - } + /** + * Constructs an instance. + */ + public MakeState(int initialSize) { + result = new ArrayList<Entry>(initialSize); + nullResultCount = 0; + regs = null; + endIndices = null; + lastAddress = 0; } /** - * Constructs an instance for the given method, based on the given - * block order and intermediate local information. + * Checks the address and other vitals as a prerequisite to + * further processing. * - * @param insns {@code non-null;} instructions to convert - * @return {@code non-null;} the constructed list + * @param address {@code >= 0;} address about to be processed + * @param reg {@code >= 0;} register number about to be processed */ - public static LocalList make(DalvInsnList insns) { - int sz = insns.size(); + private void aboutToProcess(int address, int reg) { + boolean first = (endIndices == null); - /* - * Go through the insn list, looking for all the local - * variable pseudoinstructions, splitting out LocalSnapshots - * into separate per-variable starts, adding explicit ends - * wherever a variable is replaced or moved, and collecting - * these and all the other local variable "activity" - * together into an output list (without the other insns). - * - * Note: As of this writing, this method won't be handed any - * insn lists that contain local ends, but I (danfuzz) expect - * that to change at some point, when we start feeding that - * info explicitly into the rop layer rather than only trying - * to infer it. So, given that expectation, this code is - * written to deal with them. - */ + if ((address == lastAddress) && !first) { + return; + } - MakeState state = new MakeState(sz); - - for (int i = 0; i < sz; i++) { - DalvInsn insn = insns.get(i); - - if (insn instanceof LocalSnapshot) { - RegisterSpecSet snapshot = - ((LocalSnapshot) insn).getLocals(); - state.snapshot(insn.getAddress(), snapshot); - } else if (insn instanceof LocalStart) { - RegisterSpec local = ((LocalStart) insn).getLocal(); - state.startLocal(insn.getAddress(), local); - } else if (insn instanceof LocalEnd) { - RegisterSpec local = ((LocalEnd) insn).getLocal(); - state.endLocal(insn.getAddress(), local); - } - } + if (address < lastAddress) { + throw new RuntimeException("shouldn't happen"); + } - LocalList result = state.finish(); - - if (DEBUG) { - debugVerify(result); + if (first || (reg >= endIndices.length)) { + /* + * This is the first allocation of the state set and + * index array, or we need to grow. (The latter doesn't + * happen much; in fact, we have only ever observed + * it happening in test cases, never in "real" code.) + */ + int newSz = reg + 1; + RegisterSpecSet newRegs = new RegisterSpecSet(newSz); + int[] newEnds = new int[newSz]; + Arrays.fill(newEnds, -1); + + if (!first) { + newRegs.putAll(regs); + System.arraycopy(endIndices, 0, newEnds, 0, endIndices.length); } - return result; + regs = newRegs; + endIndices = newEnds; + } } /** - * Debugging helper that verifies the constraint that a list doesn't - * contain any redundant local starts and that local ends that are - * due to replacements are properly annotated. + * Sets the local state at the given address to the given snapshot. + * The first call on this instance must be to this method, so that + * the register state can be properly sized. + * + * @param address {@code >= 0;} the address + * @param specs {@code non-null;} spec set representing the locals */ - private static void debugVerify(LocalList locals) { - try { - debugVerify0(locals); - } catch (RuntimeException ex) { - int sz = locals.size(); - for (int i = 0; i < sz; i++) { - System.err.println(locals.get(i)); - } - throw ex; + public void snapshot(int address, RegisterSpecSet specs) { + if (DEBUG) { + System.err.printf("%04x snapshot %s\n", address, specs); + } + + int sz = specs.getMaxSize(); + aboutToProcess(address, sz - 1); + + for (int i = 0; i < sz; i++) { + RegisterSpec oldSpec = regs.get(i); + RegisterSpec newSpec = filterSpec(specs.get(i)); + + if (oldSpec == null) { + if (newSpec != null) { + startLocal(address, newSpec); + } + } else if (newSpec == null) { + endLocal(address, oldSpec); + } else if (!newSpec.equalsUsingSimpleType(oldSpec)) { + endLocal(address, oldSpec); + startLocal(address, newSpec); } + } + if (DEBUG) { + System.err.printf("%04x snapshot done\n", address); + } } /** - * Helper for {@link #debugVerify} which does most of the work. - */ - private static void debugVerify0(LocalList locals) { - int sz = locals.size(); - Entry[] active = new Entry[65536]; - - for (int i = 0; i < sz; i++) { - Entry e = locals.get(i); - int reg = e.getRegister(); - - if (e.isStart()) { - Entry already = active[reg]; - - if ((already != null) && e.matches(already)) { - throw new RuntimeException("redundant start at " + - Integer.toHexString(e.getAddress()) + ": got " + - e + "; had " + already); - } - - active[reg] = e; - } else { - if (active[reg] == null) { - throw new RuntimeException("redundant end at " + - Integer.toHexString(e.getAddress())); - } - - int addr = e.getAddress(); - boolean foundStart = false; - - for (int j = i + 1; j < sz; j++) { - Entry test = locals.get(j); - if (test.getAddress() != addr) { - break; - } - if (test.getRegisterSpec().getReg() == reg) { - if (test.isStart()) { - if (e.getDisposition() - != Disposition.END_REPLACED) { - throw new RuntimeException( - "improperly marked end at " + - Integer.toHexString(addr)); - } - foundStart = true; - } else { - throw new RuntimeException( - "redundant end at " + - Integer.toHexString(addr)); - } - } - } - - if (!foundStart && - (e.getDisposition() == Disposition.END_REPLACED)) { - throw new RuntimeException( - "improper end replacement claim at " + - Integer.toHexString(addr)); - } - - active[reg] = null; - } - } - } - - /** - * Intermediate state when constructing a local list. + * Starts a local at the given address. + * + * @param address {@code >= 0;} the address + * @param startedLocal {@code non-null;} spec representing the + * started local */ - public static class MakeState { - /** {@code non-null;} result being collected */ - private final ArrayList<Entry> result; + public void startLocal(int address, RegisterSpec startedLocal) { + if (DEBUG) { + System.err.printf("%04x start %s\n", address, startedLocal); + } - /** - * {@code >= 0;} running count of nulled result entries, to help with - * sizing the final list - */ - private int nullResultCount; + int regNum = startedLocal.getReg(); - /** {@code null-ok;} current register mappings */ - private RegisterSpecSet regs; + startedLocal = filterSpec(startedLocal); + aboutToProcess(address, regNum); - /** {@code null-ok;} result indices where local ends are stored */ - private int[] endIndices; + RegisterSpec existingLocal = regs.get(regNum); - /** {@code >= 0;} last address seen */ - private int lastAddress; + if (startedLocal.equalsUsingSimpleType(existingLocal)) { + // Silently ignore a redundant start. + return; + } - /** - * Constructs an instance. + RegisterSpec movedLocal = regs.findMatchingLocal(startedLocal); + if (movedLocal != null) { + /* + * The same variable was moved from one register to another. + * So add an end for its old location. */ - public MakeState(int initialSize) { - result = new ArrayList<Entry>(initialSize); - nullResultCount = 0; - regs = null; - endIndices = null; - lastAddress = 0; - } + addOrUpdateEnd(address, Disposition.END_MOVED, movedLocal); + } - /** - * Checks the address and other vitals as a prerequisite to - * further processing. - * - * @param address {@code >= 0;} address about to be processed - * @param reg {@code >= 0;} register number about to be processed - */ - private void aboutToProcess(int address, int reg) { - boolean first = (endIndices == null); + int endAt = endIndices[regNum]; - if ((address == lastAddress) && !first) { - return; - } - - if (address < lastAddress) { - throw new RuntimeException("shouldn't happen"); - } - - if (first || (reg >= endIndices.length)) { - /* - * This is the first allocation of the state set and - * index array, or we need to grow. (The latter doesn't - * happen much; in fact, we have only ever observed - * it happening in test cases, never in "real" code.) - */ - int newSz = reg + 1; - RegisterSpecSet newRegs = new RegisterSpecSet(newSz); - int[] newEnds = new int[newSz]; - Arrays.fill(newEnds, -1); - - if (!first) { - newRegs.putAll(regs); - System.arraycopy(endIndices, 0, newEnds, 0, - endIndices.length); - } - - regs = newRegs; - endIndices = newEnds; - } - } - - /** - * Sets the local state at the given address to the given snapshot. - * The first call on this instance must be to this method, so that - * the register state can be properly sized. - * - * @param address {@code >= 0;} the address - * @param specs {@code non-null;} spec set representing the locals + if (existingLocal != null) { + /* + * There is an existing (but non-matching) local. + * Add an explicit end for it. */ - public void snapshot(int address, RegisterSpecSet specs) { - if (DEBUG) { - System.err.printf("%04x snapshot %s\n", address, specs); - } - - int sz = specs.getMaxSize(); - aboutToProcess(address, sz - 1); - - for (int i = 0; i < sz; i++) { - RegisterSpec oldSpec = regs.get(i); - RegisterSpec newSpec = filterSpec(specs.get(i)); - - if (oldSpec == null) { - if (newSpec != null) { - startLocal(address, newSpec); - } - } else if (newSpec == null) { - endLocal(address, oldSpec); - } else if (! newSpec.equalsUsingSimpleType(oldSpec)) { - endLocal(address, oldSpec); - startLocal(address, newSpec); - } - } - - if (DEBUG) { - System.err.printf("%04x snapshot done\n", address); - } - } - - /** - * Starts a local at the given address. - * - * @param address {@code >= 0;} the address - * @param startedLocal {@code non-null;} spec representing the - * started local + add(address, Disposition.END_REPLACED, existingLocal); + } else if (endAt >= 0) { + /* + * Look for an end local for the same register at the + * same address. If found, then update it or delete + * it, depending on whether or not it represents the + * same variable as the one being started. */ - public void startLocal(int address, RegisterSpec startedLocal) { - if (DEBUG) { - System.err.printf("%04x start %s\n", address, startedLocal); - } - - int regNum = startedLocal.getReg(); - - startedLocal = filterSpec(startedLocal); - aboutToProcess(address, regNum); - - RegisterSpec existingLocal = regs.get(regNum); - - if (startedLocal.equalsUsingSimpleType(existingLocal)) { - // Silently ignore a redundant start. - return; - } - - RegisterSpec movedLocal = regs.findMatchingLocal(startedLocal); - if (movedLocal != null) { - /* - * The same variable was moved from one register to another. - * So add an end for its old location. - */ - addOrUpdateEnd(address, Disposition.END_MOVED, movedLocal); - } - - int endAt = endIndices[regNum]; - - if (existingLocal != null) { - /* - * There is an existing (but non-matching) local. - * Add an explicit end for it. - */ - add(address, Disposition.END_REPLACED, existingLocal); - } else if (endAt >= 0) { - /* - * Look for an end local for the same register at the - * same address. If found, then update it or delete - * it, depending on whether or not it represents the - * same variable as the one being started. - */ - Entry endEntry = result.get(endAt); - if (endEntry.getAddress() == address) { - if (endEntry.matches(startedLocal)) { - /* - * There was already an end local for the same - * variable at the same address. This turns - * out to be superfluous, as we are starting - * up the exact same local. This situation can - * happen when a single local variable got - * somehow "split up" during intermediate - * processing. In any case, rather than represent - * the end-then-start, just remove the old end. - */ - result.set(endAt, null); - nullResultCount++; - regs.put(startedLocal); - endIndices[regNum] = -1; - return; - } else { - /* - * There was a different variable ended at the - * same address. Update it to indicate that - * it was ended due to a replacement (rather than - * ending for no particular reason). - */ - endEntry = endEntry.withDisposition( - Disposition.END_REPLACED); - result.set(endAt, endEntry); - } - } - } - - /* - * The code above didn't find and remove an unnecessary - * local end, so we now have to add one or more entries to - * the output to capture the transition. - */ - + Entry endEntry = result.get(endAt); + if (endEntry.getAddress() == address) { + if (endEntry.matches(startedLocal)) { /* - * If the local just below (in the register set at reg-1) - * is of category-2, then it is ended by this new start. + * There was already an end local for the same + * variable at the same address. This turns + * out to be superfluous, as we are starting + * up the exact same local. This situation can + * happen when a single local variable got + * somehow "split up" during intermediate + * processing. In any case, rather than represent + * the end-then-start, just remove the old end. */ - if (regNum > 0) { - RegisterSpec justBelow = regs.get(regNum - 1); - if ((justBelow != null) && justBelow.isCategory2()) { - addOrUpdateEnd(address, - Disposition.END_CLOBBERED_BY_NEXT, - justBelow); - } - } - - /* - * Similarly, if this local is category-2, then the local - * just above (if any) is ended by the start now being - * emitted. - */ - if (startedLocal.isCategory2()) { - RegisterSpec justAbove = regs.get(regNum + 1); - if (justAbove != null) { - addOrUpdateEnd(address, - Disposition.END_CLOBBERED_BY_PREV, - justAbove); - } - } - + result.set(endAt, null); + nullResultCount++; + regs.put(startedLocal); + endIndices[regNum] = -1; + return; + } else { /* - * TODO: Add an end for the same local in a different reg, - * if any (that is, if the local migrates from vX to vY, - * we should note that as a local end in vX). + * There was a different variable ended at the + * same address. Update it to indicate that + * it was ended due to a replacement (rather than + * ending for no particular reason). */ - - add(address, Disposition.START, startedLocal); + endEntry = endEntry.withDisposition(Disposition.END_REPLACED); + result.set(endAt, endEntry); + } } + } + + /* + * The code above didn't find and remove an unnecessary + * local end, so we now have to add one or more entries to + * the output to capture the transition. + */ - /** - * Ends a local at the given address, using the disposition - * {@code END_SIMPLY}. - * - * @param address {@code >= 0;} the address - * @param endedLocal {@code non-null;} spec representing the - * local being ended +/* + * If the local just below (in the register set at reg-1) + * is of category-2, then it is ended by this new start. */ - public void endLocal(int address, RegisterSpec endedLocal) { - endLocal(address, endedLocal, Disposition.END_SIMPLY); + if (regNum > 0) { + RegisterSpec justBelow = regs.get(regNum - 1); + if ((justBelow != null) && justBelow.isCategory2()) { + addOrUpdateEnd(address, Disposition.END_CLOBBERED_BY_NEXT, justBelow); + } + } + + /* + * Similarly, if this local is category-2, then the local + * just above (if any) is ended by the start now being + * emitted. + */ + if (startedLocal.isCategory2()) { + RegisterSpec justAbove = regs.get(regNum + 1); + if (justAbove != null) { + addOrUpdateEnd(address, Disposition.END_CLOBBERED_BY_PREV, justAbove); } + } - /** - * Ends a local at the given address. - * - * @param address {@code >= 0;} the address - * @param endedLocal {@code non-null;} spec representing the - * local being ended - * @param disposition reason for the end - */ - public void endLocal(int address, RegisterSpec endedLocal, - Disposition disposition) { - if (DEBUG) { - System.err.printf("%04x end %s\n", address, endedLocal); - } + /* + * TODO(dx team): Add an end for the same local in a different reg, + * if any (that is, if the local migrates from vX to vY, + * we should note that as a local end in vX). + */ - int regNum = endedLocal.getReg(); +add(address, Disposition.START, startedLocal); + } - endedLocal = filterSpec(endedLocal); - aboutToProcess(address, regNum); + /** + * Ends a local at the given address, using the disposition + * {@code END_SIMPLY}. + * + * @param address {@code >= 0;} the address + * @param endedLocal {@code non-null;} spec representing the + * local being ended + */ + public void endLocal(int address, RegisterSpec endedLocal) { + endLocal(address, endedLocal, Disposition.END_SIMPLY); + } - int endAt = endIndices[regNum]; + /** + * Ends a local at the given address. + * + * @param address {@code >= 0;} the address + * @param endedLocal {@code non-null;} spec representing the + * local being ended + * @param disposition reason for the end + */ + public void endLocal(int address, RegisterSpec endedLocal, Disposition disposition) { + if (DEBUG) { + System.err.printf("%04x end %s\n", address, endedLocal); + } - if (endAt >= 0) { - /* - * The local in the given register is already ended. - * Silently return without adding anything to the result. - */ - return; - } + int regNum = endedLocal.getReg(); - // Check for start and end at the same address. - if (checkForEmptyRange(address, endedLocal)) { - return; - } + endedLocal = filterSpec(endedLocal); + aboutToProcess(address, regNum); - add(address, disposition, endedLocal); - } + int endAt = endIndices[regNum]; - /** - * Helper for {@link #endLocal}, which handles the cases where - * and end local is issued at the same address as a start local - * for the same register. If this case is found, then this - * method will remove the start (as the local was never actually - * active), update the {@link #endIndices} to be accurate, and - * if needed update the newly-active end to reflect an altered - * disposition. - * - * @param address {@code >= 0;} the address - * @param endedLocal {@code non-null;} spec representing the - * local being ended - * @return {@code true} iff this method found the case in question - * and adjusted things accordingly + if (endAt >= 0) { + /* + * The local in the given register is already ended. + * Silently return without adding anything to the result. */ - private boolean checkForEmptyRange(int address, - RegisterSpec endedLocal) { - int at = result.size() - 1; - Entry entry; - - // Look for a previous entry at the same address. - for (/*at*/; at >= 0; at--) { - entry = result.get(at); - - if (entry == null) { - continue; - } - - if (entry.getAddress() != address) { - // We didn't find any match at the same address. - return false; - } - - if (entry.matches(endedLocal)) { - break; - } - } + return; + } - /* - * In fact, we found that the endedLocal had started at the - * same address, so do all the requisite cleanup. - */ + // Check for start and end at the same address. + if (checkForEmptyRange(address, endedLocal)) { + return; + } - regs.remove(endedLocal); - result.set(at, null); - nullResultCount++; - - int regNum = endedLocal.getReg(); - boolean found = false; - entry = null; - - // Now look back further to update where the register ended. - for (at--; at >= 0; at--) { - entry = result.get(at); - - if (entry == null) { - continue; - } + add(address, disposition, endedLocal); + } - if (entry.getRegisterSpec().getReg() == regNum) { - found = true; - break; - } - } + /** + * Helper for {@link #endLocal}, which handles the cases where + * and end local is issued at the same address as a start local + * for the same register. If this case is found, then this + * method will remove the start (as the local was never actually + * active), update the {@link #endIndices} to be accurate, and + * if needed update the newly-active end to reflect an altered + * disposition. + * + * @param address {@code >= 0;} the address + * @param endedLocal {@code non-null;} spec representing the + * local being ended + * @return {@code true} iff this method found the case in question + * and adjusted things accordingly + */ + private boolean checkForEmptyRange(int address, RegisterSpec endedLocal) { + int at = result.size() - 1; + Entry entry; - if (found) { - // We found an end for the same register. - endIndices[regNum] = at; - - if (entry.getAddress() == address) { - /* - * It's still the same address, so update the - * disposition. - */ - result.set(at, - entry.withDisposition(Disposition.END_SIMPLY)); - } - } + // Look for a previous entry at the same address. + for (/*at*/; at >= 0; at--) { + entry = result.get(at); - return true; + if (entry == null) { + continue; } - /** - * Converts a given spec into the form acceptable for use in a - * local list. This, in particular, transforms the "known - * null" type into simply {@code Object}. This method needs to - * be called for any spec that is on its way into a locals - * list. - * - * <p>This isn't necessarily the cleanest way to achieve the - * goal of not representing known nulls in a locals list, but - * it gets the job done.</p> - * - * @param orig {@code null-ok;} the original spec - * @return {@code null-ok;} an appropriately modified spec, or the - * original if nothing needs to be done - */ - private static RegisterSpec filterSpec(RegisterSpec orig) { - if ((orig != null) && (orig.getType() == Type.KNOWN_NULL)) { - return orig.withType(Type.OBJECT); - } + if (entry.getAddress() != address) { + // We didn't find any match at the same address. + return false; + } - return orig; + if (entry.matches(endedLocal)) { + break; } + } - /** - * Adds an entry to the result, updating the adjunct tables - * accordingly. - * - * @param address {@code >= 0;} the address - * @param disposition {@code non-null;} the disposition - * @param spec {@code non-null;} spec representing the local - */ - private void add(int address, Disposition disposition, - RegisterSpec spec) { - int regNum = spec.getReg(); + /* + * In fact, we found that the endedLocal had started at the + * same address, so do all the requisite cleanup. + */ - result.add(new Entry(address, disposition, spec)); +regs.remove(endedLocal); + result.set(at, null); + nullResultCount++; - if (disposition == Disposition.START) { - regs.put(spec); - endIndices[regNum] = -1; - } else { - regs.remove(spec); - endIndices[regNum] = result.size() - 1; - } - } + int regNum = endedLocal.getReg(); + boolean found = false; + entry = null; - /** - * Adds or updates an end local (changing its disposition). If - * this would cause an empty range for a local, this instead - * removes the local entirely. - * - * @param address {@code >= 0;} the address - * @param disposition {@code non-null;} the disposition - * @param spec {@code non-null;} spec representing the local - */ - private void addOrUpdateEnd(int address, Disposition disposition, - RegisterSpec spec) { - if (disposition == Disposition.START) { - throw new RuntimeException("shouldn't happen"); - } + // Now look back further to update where the register ended. + for (at--; at >= 0; at--) { + entry = result.get(at); - int regNum = spec.getReg(); - int endAt = endIndices[regNum]; - - if (endAt >= 0) { - // There is a previous end. - Entry endEntry = result.get(endAt); - if ((endEntry.getAddress() == address) && - endEntry.getRegisterSpec().equals(spec)) { - /* - * The end is for the right address and variable, so - * update it. - */ - result.set(endAt, endEntry.withDisposition(disposition)); - regs.remove(spec); // TODO: Is this line superfluous? - return; - } - } + if (entry == null) { + continue; + } - endLocal(address, spec, disposition); + if (entry.getRegisterSpec().getReg() == regNum) { + found = true; + break; + } + } + + if (found) { + // We found an end for the same register. + endIndices[regNum] = at; + + if (entry.getAddress() == address) { + /* + * It's still the same address, so update the + * disposition. + */ + result.set(at, entry.withDisposition(Disposition.END_SIMPLY)); } + } - /** - * Finishes processing altogether and gets the result. - * - * @return {@code non-null;} the result list - */ - public LocalList finish() { - aboutToProcess(Integer.MAX_VALUE, 0); + return true; + } - int resultSz = result.size(); - int finalSz = resultSz - nullResultCount; + /** + * Converts a given spec into the form acceptable for use in a + * local list. This, in particular, transforms the "known + * null" type into simply {@code Object}. This method needs to + * be called for any spec that is on its way into a locals + * list. + * + * <p>This isn't necessarily the cleanest way to achieve the + * goal of not representing known nulls in a locals list, but + * it gets the job done.</p> + * + * @param orig {@code null-ok;} the original spec + * @return {@code null-ok;} an appropriately modified spec, or the + * original if nothing needs to be done + */ + private static RegisterSpec filterSpec(RegisterSpec orig) { + if ((orig != null) && (orig.getType() == Type.KNOWN_NULL)) { + return orig.withType(Type.OBJECT); + } - if (finalSz == 0) { - return EMPTY; - } + return orig; + } - /* - * Collect an array of only the non-null entries, and then - * sort it to get a consistent order for everything: Local - * ends and starts for a given address could come in any - * order, but we want ends before starts as well as - * registers in order (within ends or starts). - */ + /** + * Adds an entry to the result, updating the adjunct tables + * accordingly. + * + * @param address {@code >= 0;} the address + * @param disposition {@code non-null;} the disposition + * @param spec {@code non-null;} spec representing the local + */ + private void add(int address, Disposition disposition, RegisterSpec spec) { + int regNum = spec.getReg(); + + result.add(new Entry(address, disposition, spec)); + + if (disposition == Disposition.START) { + regs.put(spec); + endIndices[regNum] = -1; + } else { + regs.remove(spec); + endIndices[regNum] = result.size() - 1; + } + } - Entry[] resultArr = new Entry[finalSz]; + /** + * Adds or updates an end local (changing its disposition). If + * this would cause an empty range for a local, this instead + * removes the local entirely. + * + * @param address {@code >= 0;} the address + * @param disposition {@code non-null;} the disposition + * @param spec {@code non-null;} spec representing the local + */ + private void addOrUpdateEnd(int address, Disposition disposition, RegisterSpec spec) { + if (disposition == Disposition.START) { + throw new RuntimeException("shouldn't happen"); + } + + int regNum = spec.getReg(); + int endAt = endIndices[regNum]; + + if (endAt >= 0) { + // There is a previous end. + Entry endEntry = result.get(endAt); + if ((endEntry.getAddress() == address) && endEntry.getRegisterSpec().equals(spec)) { + /* + * The end is for the right address and variable, so + * update it. + */ + result.set(endAt, endEntry.withDisposition(disposition)); + regs.remove(spec); // TODO(dx team): Is this line superfluous? + return; + } + } - if (resultSz == finalSz) { - result.toArray(resultArr); - } else { - int at = 0; - for (Entry e : result) { - if (e != null) { - resultArr[at++] = e; - } - } - } + endLocal(address, spec, disposition); + } - Arrays.sort(resultArr); + /** + * Finishes processing altogether and gets the result. + * + * @return {@code non-null;} the result list + */ + public LocalList finish() { + aboutToProcess(Integer.MAX_VALUE, 0); + + int resultSz = result.size(); + int finalSz = resultSz - nullResultCount; + + if (finalSz == 0) { + return EMPTY; + } + + /* + * Collect an array of only the non-null entries, and then + * sort it to get a consistent order for everything: Local + * ends and starts for a given address could come in any + * order, but we want ends before starts as well as + * registers in order (within ends or starts). + */ + +Entry[] resultArr = new Entry[finalSz]; + + if (resultSz == finalSz) { + result.toArray(resultArr); + } else { + int at = 0; + for (Entry e : result) { + if (e != null) { + resultArr[at++] = e; + } + } + } - LocalList resultList = new LocalList(finalSz); + Arrays.sort(resultArr); - for (int i = 0; i < finalSz; i++) { - resultList.set(i, resultArr[i]); - } + LocalList resultList = new LocalList(finalSz); - resultList.setImmutable(); - return resultList; - } + for (int i = 0; i < finalSz; i++) { + resultList.set(i, resultArr[i]); + } + + resultList.setImmutable(); + return resultList; } + } } diff --git a/dx/src/com/android/jack/dx/dex/code/LocalSnapshot.java b/dx/src/com/android/jack/dx/dex/code/LocalSnapshot.java index 01f3943..d14a9a2 100644 --- a/dx/src/com/android/jack/dx/dex/code/LocalSnapshot.java +++ b/dx/src/com/android/jack/dx/dex/code/LocalSnapshot.java @@ -27,70 +27,70 @@ import com.android.jack.dx.rop.code.SourcePosition; * the instance in an instruction array. */ public final class LocalSnapshot extends ZeroSizeInsn { - /** {@code non-null;} local state associated with this instance */ - private final RegisterSpecSet locals; + /** {@code non-null;} local state associated with this instance */ + private final RegisterSpecSet locals; - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - * @param locals {@code non-null;} associated local variable state - */ - public LocalSnapshot(SourcePosition position, RegisterSpecSet locals) { - super(position); + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + * @param locals {@code non-null;} associated local variable state + */ + public LocalSnapshot(SourcePosition position, RegisterSpecSet locals) { + super(position); - if (locals == null) { - throw new NullPointerException("locals == null"); - } - - this.locals = locals; + if (locals == null) { + throw new NullPointerException("locals == null"); } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisterOffset(int delta) { - return new LocalSnapshot(getPosition(), locals.withOffset(delta)); - } + this.locals = locals; + } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new LocalSnapshot(getPosition(), locals); - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisterOffset(int delta) { + return new LocalSnapshot(getPosition(), locals.withOffset(delta)); + } - /** - * Gets the local state associated with this instance. - * - * @return {@code non-null;} the state - */ - public RegisterSpecSet getLocals() { - return locals; - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new LocalSnapshot(getPosition(), locals); + } - /** {@inheritDoc} */ - @Override - protected String argString() { - return locals.toString(); - } + /** + * Gets the local state associated with this instance. + * + * @return {@code non-null;} the state + */ + public RegisterSpecSet getLocals() { + return locals; + } - /** {@inheritDoc} */ - @Override - protected String listingString0(boolean noteIndices) { - int sz = locals.size(); - int max = locals.getMaxSize(); - StringBuffer sb = new StringBuffer(100 + sz * 40); + /** {@inheritDoc} */ + @Override + protected String argString() { + return locals.toString(); + } - sb.append("local-snapshot"); + /** {@inheritDoc} */ + @Override + protected String listingString0(boolean noteIndices) { + int sz = locals.size(); + int max = locals.getMaxSize(); + StringBuffer sb = new StringBuffer(100 + sz * 40); - for (int i = 0; i < max; i++) { - RegisterSpec spec = locals.get(i); - if (spec != null) { - sb.append("\n "); - sb.append(LocalStart.localString(spec)); - } - } + sb.append("local-snapshot"); - return sb.toString(); + for (int i = 0; i < max; i++) { + RegisterSpec spec = locals.get(i); + if (spec != null) { + sb.append("\n "); + sb.append(LocalStart.localString(spec)); + } } + + return sb.toString(); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/LocalStart.java b/dx/src/com/android/jack/dx/dex/code/LocalStart.java index 9b8a400..5d5f6ab 100644 --- a/dx/src/com/android/jack/dx/dex/code/LocalStart.java +++ b/dx/src/com/android/jack/dx/dex/code/LocalStart.java @@ -27,72 +27,72 @@ import com.android.jack.dx.rop.code.SourcePosition; * is bound. */ public final class LocalStart extends ZeroSizeInsn { - /** - * {@code non-null;} register spec representing the local variable introduced - * by this instance - */ - private final RegisterSpec local; + /** + * {@code non-null;} register spec representing the local variable introduced + * by this instance + */ + private final RegisterSpec local; - /** - * Returns the local variable listing string for a single register spec. - * - * @param spec {@code non-null;} the spec to convert - * @return {@code non-null;} the string form - */ - public static String localString(RegisterSpec spec) { - return spec.regString() + ' ' + spec.getLocalItem().toString() + ": " + - spec.getTypeBearer().toHuman(); - } - - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - * @param local {@code non-null;} register spec representing the local - * variable introduced by this instance - */ - public LocalStart(SourcePosition position, RegisterSpec local) { - super(position); + /** + * Returns the local variable listing string for a single register spec. + * + * @param spec {@code non-null;} the spec to convert + * @return {@code non-null;} the string form + */ + public static String localString(RegisterSpec spec) { + return spec.regString() + ' ' + spec.getLocalItem().toString() + ": " + + spec.getTypeBearer().toHuman(); + } - if (local == null) { - throw new NullPointerException("local == null"); - } + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + * @param local {@code non-null;} register spec representing the local + * variable introduced by this instance + */ + public LocalStart(SourcePosition position, RegisterSpec local) { + super(position); - this.local = local; + if (local == null) { + throw new NullPointerException("local == null"); } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisterOffset(int delta) { - return new LocalStart(getPosition(), local.withOffset(delta)); - } + this.local = local; + } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new LocalStart(getPosition(), local); - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisterOffset(int delta) { + return new LocalStart(getPosition(), local.withOffset(delta)); + } - /** - * Gets the register spec representing the local variable introduced - * by this instance. - * - * @return {@code non-null;} the register spec - */ - public RegisterSpec getLocal() { - return local; - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new LocalStart(getPosition(), local); + } - /** {@inheritDoc} */ - @Override - protected String argString() { - return local.toString(); - } + /** + * Gets the register spec representing the local variable introduced + * by this instance. + * + * @return {@code non-null;} the register spec + */ + public RegisterSpec getLocal() { + return local; + } - /** {@inheritDoc} */ - @Override - protected String listingString0(boolean noteIndices) { - return "local-start " + localString(local); - } + /** {@inheritDoc} */ + @Override + protected String argString() { + return local.toString(); + } + + /** {@inheritDoc} */ + @Override + protected String listingString0(boolean noteIndices) { + return "local-start " + localString(local); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/OddSpacer.java b/dx/src/com/android/jack/dx/dex/code/OddSpacer.java index c9166f5..8924659 100644 --- a/dx/src/com/android/jack/dx/dex/code/OddSpacer.java +++ b/dx/src/com/android/jack/dx/dex/code/OddSpacer.java @@ -28,49 +28,49 @@ import com.android.jack.dx.util.AnnotatedOutput; * require it. */ public final class OddSpacer extends VariableSizeInsn { - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - */ - public OddSpacer(SourcePosition position) { - super(position, RegisterSpecList.EMPTY); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return (getAddress() & 1); - } + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + */ + public OddSpacer(SourcePosition position) { + super(position, RegisterSpecList.EMPTY); + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out) { - if (codeSize() != 0) { - out.writeShort(InsnFormat.codeUnit(Opcodes.NOP, 0)); - } - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return (getAddress() & 1); + } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new OddSpacer(getPosition()); + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out) { + if (codeSize() != 0) { + out.writeShort(InsnFormat.codeUnit(Opcodes.NOP, 0)); } + } - /** {@inheritDoc} */ - @Override - protected String argString() { - return null; - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new OddSpacer(getPosition()); + } - /** {@inheritDoc} */ - @Override - protected String listingString0(boolean noteIndices) { - if (codeSize() == 0) { - return null; - } + /** {@inheritDoc} */ + @Override + protected String argString() { + return null; + } - return "nop // spacer"; + /** {@inheritDoc} */ + @Override + protected String listingString0(boolean noteIndices) { + if (codeSize() == 0) { + return null; } + + return "nop // spacer"; + } } diff --git a/dx/src/com/android/jack/dx/dex/code/OutputCollector.java b/dx/src/com/android/jack/dx/dex/code/OutputCollector.java index 3225b24..6c12f4c 100644 --- a/dx/src/com/android/jack/dx/dex/code/OutputCollector.java +++ b/dx/src/com/android/jack/dx/dex/code/OutputCollector.java @@ -29,93 +29,93 @@ import java.util.ArrayList; * instance. */ public final class OutputCollector { - /** - * {@code non-null;} the associated finisher (which holds the instruction - * list in-progress) - */ - private final OutputFinisher finisher; + /** + * {@code non-null;} the associated finisher (which holds the instruction + * list in-progress) + */ + private final OutputFinisher finisher; - /** - * {@code null-ok;} suffix for the output, or {@code null} if the suffix - * has been appended to the main output (by {@link #appendSuffixToOutput}) - */ - private ArrayList<DalvInsn> suffix; + /** + * {@code null-ok;} suffix for the output, or {@code null} if the suffix + * has been appended to the main output (by {@link #appendSuffixToOutput}) + */ + private ArrayList<DalvInsn> suffix; - /** - * Constructs an instance. - * - * @param dexOptions {@code non-null;} options for dex output - * @param initialCapacity {@code >= 0;} initial capacity of the output list - * @param suffixInitialCapacity {@code >= 0;} initial capacity of the output - * suffix - * @param regCount {@code >= 0;} register count for the method - */ - public OutputCollector(DexOptions dexOptions, int initialCapacity, int suffixInitialCapacity, - int regCount) { - this.finisher = new OutputFinisher(dexOptions, initialCapacity, regCount); - this.suffix = new ArrayList<DalvInsn>(suffixInitialCapacity); - } - - /** - * Adds an instruction to the output. - * - * @param insn {@code non-null;} the instruction to add - */ - public void add(DalvInsn insn) { - finisher.add(insn); - } + /** + * Constructs an instance. + * + * @param dexOptions {@code non-null;} options for dex output + * @param initialCapacity {@code >= 0;} initial capacity of the output list + * @param suffixInitialCapacity {@code >= 0;} initial capacity of the output + * suffix + * @param regCount {@code >= 0;} register count for the method + */ + public OutputCollector(DexOptions dexOptions, int initialCapacity, int suffixInitialCapacity, + int regCount) { + this.finisher = new OutputFinisher(dexOptions, initialCapacity, regCount); + this.suffix = new ArrayList<DalvInsn>(suffixInitialCapacity); + } - /** - * Reverses a branch which is buried a given number of instructions - * backward in the output. It is illegal to call this unless the - * indicated instruction really is a reversible branch. - * - * @param which how many instructions back to find the branch; - * {@code 0} is the most recently added instruction, - * {@code 1} is the instruction before that, etc. - * @param newTarget {@code non-null;} the new target for the reversed branch - */ - public void reverseBranch(int which, CodeAddress newTarget) { - finisher.reverseBranch(which, newTarget); - } + /** + * Adds an instruction to the output. + * + * @param insn {@code non-null;} the instruction to add + */ + public void add(DalvInsn insn) { + finisher.add(insn); + } - /** - * Adds an instruction to the output suffix. - * - * @param insn {@code non-null;} the instruction to add - */ - public void addSuffix(DalvInsn insn) { - suffix.add(insn); - } + /** + * Reverses a branch which is buried a given number of instructions + * backward in the output. It is illegal to call this unless the + * indicated instruction really is a reversible branch. + * + * @param which how many instructions back to find the branch; + * {@code 0} is the most recently added instruction, + * {@code 1} is the instruction before that, etc. + * @param newTarget {@code non-null;} the new target for the reversed branch + */ + public void reverseBranch(int which, CodeAddress newTarget) { + finisher.reverseBranch(which, newTarget); + } - /** - * Gets the results of all the calls on this instance, in the form of - * an {@link OutputFinisher}. - * - * @return {@code non-null;} the output finisher - * @throws UnsupportedOperationException if this method has - * already been called - */ - public OutputFinisher getFinisher() { - if (suffix == null) { - throw new UnsupportedOperationException("already processed"); - } + /** + * Adds an instruction to the output suffix. + * + * @param insn {@code non-null;} the instruction to add + */ + public void addSuffix(DalvInsn insn) { + suffix.add(insn); + } - appendSuffixToOutput(); - return finisher; + /** + * Gets the results of all the calls on this instance, in the form of + * an {@link OutputFinisher}. + * + * @return {@code non-null;} the output finisher + * @throws UnsupportedOperationException if this method has + * already been called + */ + public OutputFinisher getFinisher() { + if (suffix == null) { + throw new UnsupportedOperationException("already processed"); } - /** - * Helper for {@link #getFinisher}, which appends the suffix to - * the primary output. - */ - private void appendSuffixToOutput() { - int size = suffix.size(); + appendSuffixToOutput(); + return finisher; + } - for (int i = 0; i < size; i++) { - finisher.add(suffix.get(i)); - } + /** + * Helper for {@link #getFinisher}, which appends the suffix to + * the primary output. + */ + private void appendSuffixToOutput() { + int size = suffix.size(); - suffix = null; + for (int i = 0; i < size; i++) { + finisher.add(suffix.get(i)); } + + suffix = null; + } } diff --git a/dx/src/com/android/jack/dx/dex/code/OutputFinisher.java b/dx/src/com/android/jack/dx/dex/code/OutputFinisher.java index f004b02..ac3923d 100644 --- a/dx/src/com/android/jack/dx/dex/code/OutputFinisher.java +++ b/dx/src/com/android/jack/dx/dex/code/OutputFinisher.java @@ -41,744 +41,735 @@ import java.util.HashSet; * form of a {@link DalvInsnList} instance. */ public final class OutputFinisher { - /** {@code non-null;} options for dex output */ - private final DexOptions dexOptions; - - /** - * {@code >= 0;} register count for the method, not including any extra - * "reserved" registers needed to translate "difficult" instructions - */ - private final int unreservedRegCount; - - /** {@code non-null;} the list of instructions, per se */ - private ArrayList<DalvInsn> insns; - - /** whether any instruction has position info */ - private boolean hasAnyPositionInfo; - - /** whether any instruction has local variable info */ - private boolean hasAnyLocalInfo; - - /** - * {@code >= 0;} the count of reserved registers (low-numbered - * registers used when expanding instructions that can't be - * represented simply); becomes valid after a call to {@link - * #massageInstructions} - */ - private int reservedCount; - - /** - * Constructs an instance. It initially contains no instructions. - * - * @param dexOptions {@code non-null;} options for dex output - * @param regCount {@code >= 0;} register count for the method - * @param initialCapacity {@code >= 0;} initial capacity of the - * instructions list - */ - public OutputFinisher(DexOptions dexOptions, int initialCapacity, int regCount) { - this.dexOptions = dexOptions; - this.unreservedRegCount = regCount; - this.insns = new ArrayList<DalvInsn>(initialCapacity); - this.reservedCount = -1; - this.hasAnyPositionInfo = false; - this.hasAnyLocalInfo = false; + /** {@code non-null;} options for dex output */ + private final DexOptions dexOptions; + + /** + * {@code >= 0;} register count for the method, not including any extra + * "reserved" registers needed to translate "difficult" instructions + */ + private final int unreservedRegCount; + + /** {@code non-null;} the list of instructions, per se */ + private ArrayList<DalvInsn> insns; + + /** whether any instruction has position info */ + private boolean hasAnyPositionInfo; + + /** whether any instruction has local variable info */ + private boolean hasAnyLocalInfo; + + /** + * {@code >= 0;} the count of reserved registers (low-numbered + * registers used when expanding instructions that can't be + * represented simply); becomes valid after a call to {@link + * #massageInstructions} + */ + private int reservedCount; + + /** + * Constructs an instance. It initially contains no instructions. + * + * @param dexOptions {@code non-null;} options for dex output + * @param regCount {@code >= 0;} register count for the method + * @param initialCapacity {@code >= 0;} initial capacity of the + * instructions list + */ + public OutputFinisher(DexOptions dexOptions, int initialCapacity, int regCount) { + this.dexOptions = dexOptions; + this.unreservedRegCount = regCount; + this.insns = new ArrayList<DalvInsn>(initialCapacity); + this.reservedCount = -1; + this.hasAnyPositionInfo = false; + this.hasAnyLocalInfo = false; + } + + /** + * Returns whether any of the instructions added to this instance + * come with position info. + * + * @return whether any of the instructions added to this instance + * come with position info + */ + public boolean hasAnyPositionInfo() { + return hasAnyPositionInfo; + } + + /** + * Returns whether this instance has any local variable information. + * + * @return whether this instance has any local variable information + */ + public boolean hasAnyLocalInfo() { + return hasAnyLocalInfo; + } + + /** + * Helper for {@link #add} which scrutinizes a single + * instruction for local variable information. + * + * @param insn {@code non-null;} instruction to scrutinize + * @return {@code true} iff the instruction refers to any + * named locals + */ + private static boolean hasLocalInfo(DalvInsn insn) { + if (insn instanceof LocalSnapshot) { + RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals(); + int size = specs.size(); + for (int i = 0; i < size; i++) { + if (hasLocalInfo(specs.get(i))) { + return true; + } + } + } else if (insn instanceof LocalStart) { + RegisterSpec spec = ((LocalStart) insn).getLocal(); + if (hasLocalInfo(spec)) { + return true; + } } - /** - * Returns whether any of the instructions added to this instance - * come with position info. - * - * @return whether any of the instructions added to this instance - * come with position info - */ - public boolean hasAnyPositionInfo() { - return hasAnyPositionInfo; + return false; + } + + /** + * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single + * register spec. + * + * @param spec {@code non-null;} spec to scrutinize + * @return {@code true} iff the spec refers to any + * named locals + */ + private static boolean hasLocalInfo(RegisterSpec spec) { + return (spec != null) && (spec.getLocalItem().getName() != null); + } + + /** + * Returns the set of all constants referred to by instructions added + * to this instance. + * + * @return {@code non-null;} the set of constants + */ + public HashSet<Constant> getAllConstants() { + HashSet<Constant> result = new HashSet<Constant>(20); + + for (DalvInsn insn : insns) { + addConstants(result, insn); } - /** - * Returns whether this instance has any local variable information. - * - * @return whether this instance has any local variable information - */ - public boolean hasAnyLocalInfo() { - return hasAnyLocalInfo; + return result; + } + + /** + * Helper for {@link #getAllConstants} which adds all the info for + * a single instruction. + * + * @param result {@code non-null;} result set to add to + * @param insn {@code non-null;} instruction to scrutinize + */ + private static void addConstants(HashSet<Constant> result, DalvInsn insn) { + if (insn instanceof CstInsn) { + Constant cst = ((CstInsn) insn).getConstant(); + result.add(cst); + } else if (insn instanceof LocalSnapshot) { + RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals(); + int size = specs.size(); + for (int i = 0; i < size; i++) { + addConstants(result, specs.get(i)); + } + } else if (insn instanceof LocalStart) { + RegisterSpec spec = ((LocalStart) insn).getLocal(); + addConstants(result, spec); + } + } + + /** + * Helper for {@link #getAllConstants} which adds all the info for + * a single {@code RegisterSpec}. + * + * @param result {@code non-null;} result set to add to + * @param spec {@code null-ok;} register spec to add + */ + private static void addConstants(HashSet<Constant> result, RegisterSpec spec) { + if (spec == null) { + return; } - /** - * Helper for {@link #add} which scrutinizes a single - * instruction for local variable information. - * - * @param insn {@code non-null;} instruction to scrutinize - * @return {@code true} iff the instruction refers to any - * named locals - */ - private static boolean hasLocalInfo(DalvInsn insn) { - if (insn instanceof LocalSnapshot) { - RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals(); - int size = specs.size(); - for (int i = 0; i < size; i++) { - if (hasLocalInfo(specs.get(i))) { - return true; - } - } - } else if (insn instanceof LocalStart) { - RegisterSpec spec = ((LocalStart) insn).getLocal(); - if (hasLocalInfo(spec)) { - return true; - } - } + LocalItem local = spec.getLocalItem(); + CstString name = local.getName(); + CstString signature = local.getSignature(); + Type type = spec.getType(); + CstType localType = local.getType(); - return false; + if (type != Type.KNOWN_NULL) { + result.add(CstType.intern(type)); } - /** - * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single - * register spec. - * - * @param spec {@code non-null;} spec to scrutinize - * @return {@code true} iff the spec refers to any - * named locals - */ - private static boolean hasLocalInfo(RegisterSpec spec) { - return (spec != null) - && (spec.getLocalItem().getName() != null); + if (localType != null) { + result.add(localType); } - /** - * Returns the set of all constants referred to by instructions added - * to this instance. - * - * @return {@code non-null;} the set of constants - */ - public HashSet<Constant> getAllConstants() { - HashSet<Constant> result = new HashSet<Constant>(20); - - for (DalvInsn insn : insns) { - addConstants(result, insn); - } - - return result; + if (name != null) { + result.add(name); } - /** - * Helper for {@link #getAllConstants} which adds all the info for - * a single instruction. - * - * @param result {@code non-null;} result set to add to - * @param insn {@code non-null;} instruction to scrutinize - */ - private static void addConstants(HashSet<Constant> result, - DalvInsn insn) { - if (insn instanceof CstInsn) { - Constant cst = ((CstInsn) insn).getConstant(); - result.add(cst); - } else if (insn instanceof LocalSnapshot) { - RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals(); - int size = specs.size(); - for (int i = 0; i < size; i++) { - addConstants(result, specs.get(i)); - } - } else if (insn instanceof LocalStart) { - RegisterSpec spec = ((LocalStart) insn).getLocal(); - addConstants(result, spec); - } + if (signature != null) { + result.add(signature); } - - /** - * Helper for {@link #getAllConstants} which adds all the info for - * a single {@code RegisterSpec}. - * - * @param result {@code non-null;} result set to add to - * @param spec {@code null-ok;} register spec to add - */ - private static void addConstants(HashSet<Constant> result, - RegisterSpec spec) { - if (spec == null) { - return; - } - - LocalItem local = spec.getLocalItem(); - CstString name = local.getName(); - CstString signature = local.getSignature(); - Type type = spec.getType(); - CstType localType = local.getType(); - - if (type != Type.KNOWN_NULL) { - result.add(CstType.intern(type)); - } - - if (localType != null) { - result.add(localType); - } - - if (name != null) { - result.add(name); - } - - if (signature != null) { - result.add(signature); - } + } + + /** + * Adds an instruction to the output. + * + * @param insn {@code non-null;} the instruction to add + */ + public void add(DalvInsn insn) { + insns.add(insn); + updateInfo(insn); + } + + /** + * Inserts an instruction in the output at the given offset. + * + * @param at {@code >= 0;} what index to insert at + * @param insn {@code non-null;} the instruction to insert + */ + public void insert(int at, DalvInsn insn) { + insns.add(at, insn); + updateInfo(insn); + } + + /** + * Helper for {@link #add} and {@link #insert}, + * which updates the position and local info flags. + * + * @param insn {@code non-null;} an instruction that was just introduced + */ + private void updateInfo(DalvInsn insn) { + if (!hasAnyPositionInfo) { + SourcePosition pos = insn.getPosition(); + if (pos.getLine() >= 0) { + hasAnyPositionInfo = true; + } } - /** - * Adds an instruction to the output. - * - * @param insn {@code non-null;} the instruction to add - */ - public void add(DalvInsn insn) { - insns.add(insn); - updateInfo(insn); + if (!hasAnyLocalInfo) { + if (hasLocalInfo(insn)) { + hasAnyLocalInfo = true; + } } - - /** - * Inserts an instruction in the output at the given offset. - * - * @param at {@code >= 0;} what index to insert at - * @param insn {@code non-null;} the instruction to insert - */ - public void insert(int at, DalvInsn insn) { - insns.add(at, insn); - updateInfo(insn); + } + + /** + * Reverses a branch which is buried a given number of instructions + * backward in the output. It is illegal to call this unless the + * indicated instruction really is a reversible branch. + * + * @param which how many instructions back to find the branch; + * {@code 0} is the most recently added instruction, + * {@code 1} is the instruction before that, etc. + * @param newTarget {@code non-null;} the new target for the + * reversed branch + */ + public void reverseBranch(int which, CodeAddress newTarget) { + int size = insns.size(); + int index = size - which - 1; + TargetInsn targetInsn; + + try { + targetInsn = (TargetInsn) insns.get(index); + } catch (IndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("too few instructions"); + } catch (ClassCastException ex) { + // Translate the exception. + throw new IllegalArgumentException("non-reversible instruction"); } - /** - * Helper for {@link #add} and {@link #insert}, - * which updates the position and local info flags. - * - * @param insn {@code non-null;} an instruction that was just introduced + /* + * No need to call this.set(), since the format and other info + * are the same. */ - private void updateInfo(DalvInsn insn) { - if (! hasAnyPositionInfo) { - SourcePosition pos = insn.getPosition(); - if (pos.getLine() >= 0) { - hasAnyPositionInfo = true; - } - } - - if (! hasAnyLocalInfo) { - if (hasLocalInfo(insn)) { - hasAnyLocalInfo = true; - } - } + insns.set(index, targetInsn.withNewTargetAndReversed(newTarget)); + } + + /** + * Assigns indices in all instructions that need them, using the + * given callback to perform lookups. This should be called before + * calling {@link #finishProcessingAndGetList}. + * + * @param callback {@code non-null;} callback object + */ + public void assignIndices(DalvCode.AssignIndicesCallback callback) { + for (DalvInsn insn : insns) { + if (insn instanceof CstInsn) { + assignIndices((CstInsn) insn, callback); + } } - - /** - * Reverses a branch which is buried a given number of instructions - * backward in the output. It is illegal to call this unless the - * indicated instruction really is a reversible branch. - * - * @param which how many instructions back to find the branch; - * {@code 0} is the most recently added instruction, - * {@code 1} is the instruction before that, etc. - * @param newTarget {@code non-null;} the new target for the - * reversed branch - */ - public void reverseBranch(int which, CodeAddress newTarget) { - int size = insns.size(); - int index = size - which - 1; - TargetInsn targetInsn; - - try { - targetInsn = (TargetInsn) insns.get(index); - } catch (IndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("too few instructions"); - } catch (ClassCastException ex) { - // Translate the exception. - throw new IllegalArgumentException("non-reversible instruction"); - } - - /* - * No need to call this.set(), since the format and other info - * are the same. - */ - insns.set(index, targetInsn.withNewTargetAndReversed(newTarget)); + } + + /** + * Helper for {@link #assignIndices} which does assignment for one + * instruction. + * + * @param insn {@code non-null;} the instruction + * @param callback {@code non-null;} the callback + */ + private static void assignIndices(CstInsn insn, DalvCode.AssignIndicesCallback callback) { + Constant cst = insn.getConstant(); + int index = callback.getIndex(cst); + + if (index >= 0) { + insn.setIndex(index); } - /** - * Assigns indices in all instructions that need them, using the - * given callback to perform lookups. This should be called before - * calling {@link #finishProcessingAndGetList}. - * - * @param callback {@code non-null;} callback object - */ - public void assignIndices(DalvCode.AssignIndicesCallback callback) { - for (DalvInsn insn : insns) { - if (insn instanceof CstInsn) { - assignIndices((CstInsn) insn, callback); - } - } + if (cst instanceof CstMemberRef) { + CstMemberRef member = (CstMemberRef) cst; + CstType definer = member.getDefiningClass(); + index = callback.getIndex(definer); + if (index >= 0) { + insn.setClassIndex(index); + } } - - /** - * Helper for {@link #assignIndices} which does assignment for one - * instruction. - * - * @param insn {@code non-null;} the instruction - * @param callback {@code non-null;} the callback - */ - private static void assignIndices(CstInsn insn, - DalvCode.AssignIndicesCallback callback) { - Constant cst = insn.getConstant(); - int index = callback.getIndex(cst); - - if (index >= 0) { - insn.setIndex(index); - } - - if (cst instanceof CstMemberRef) { - CstMemberRef member = (CstMemberRef) cst; - CstType definer = member.getDefiningClass(); - index = callback.getIndex(definer); - if (index >= 0) { - insn.setClassIndex(index); - } - } + } + + /** + * Does final processing on this instance and gets the output as + * a {@link DalvInsnList}. Final processing consists of: + * + * <ul> + * <li>optionally renumbering registers (to make room as needed for + * expanded instructions)</li> + * <li>picking a final opcode for each instruction</li> + * <li>rewriting instructions, because of register number, + * constant pool index, or branch target size issues</li> + * <li>assigning final addresses</li> + * </ul> + * + * <p><b>Note:</b> This method may only be called once per instance + * of this class.</p> + * + * @return {@code non-null;} the output list + * @throws UnsupportedOperationException if this method has + * already been called + */ + public DalvInsnList finishProcessingAndGetList() { + if (reservedCount >= 0) { + throw new UnsupportedOperationException("already processed"); } - /** - * Does final processing on this instance and gets the output as - * a {@link DalvInsnList}. Final processing consists of: - * - * <ul> - * <li>optionally renumbering registers (to make room as needed for - * expanded instructions)</li> - * <li>picking a final opcode for each instruction</li> - * <li>rewriting instructions, because of register number, - * constant pool index, or branch target size issues</li> - * <li>assigning final addresses</li> - * </ul> - * - * <p><b>Note:</b> This method may only be called once per instance - * of this class.</p> - * - * @return {@code non-null;} the output list - * @throws UnsupportedOperationException if this method has - * already been called - */ - public DalvInsnList finishProcessingAndGetList() { - if (reservedCount >= 0) { - throw new UnsupportedOperationException("already processed"); - } - - Dop[] opcodes = makeOpcodesArray(); - reserveRegisters(opcodes); - massageInstructions(opcodes); - assignAddressesAndFixBranches(); - - return DalvInsnList.makeImmutable(insns, - reservedCount + unreservedRegCount); + Dop[] opcodes = makeOpcodesArray(); + reserveRegisters(opcodes); + massageInstructions(opcodes); + assignAddressesAndFixBranches(); + + return DalvInsnList.makeImmutable(insns, reservedCount + unreservedRegCount); + } + + /** + * Helper for {@link #finishProcessingAndGetList}, which extracts + * the opcode out of each instruction into a separate array, to be + * further manipulated as things progress. + * + * @return {@code non-null;} the array of opcodes + */ + private Dop[] makeOpcodesArray() { + int size = insns.size(); + Dop[] result = new Dop[size]; + + for (int i = 0; i < size; i++) { + result[i] = insns.get(i).getOpcode(); } - /** - * Helper for {@link #finishProcessingAndGetList}, which extracts - * the opcode out of each instruction into a separate array, to be - * further manipulated as things progress. - * - * @return {@code non-null;} the array of opcodes + return result; + } + + /** + * Helper for {@link #finishProcessingAndGetList}, which figures + * out how many reserved registers are required and then reserving + * them. It also updates the given {@code opcodes} array so + * as to avoid extra work when constructing the massaged + * instruction list. + * + * @param opcodes {@code non-null;} array of per-instruction + * opcode selections + */ + private void reserveRegisters(Dop[] opcodes) { + int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount; + + /* + * Call calculateReservedCount() and then perform register + * reservation, repeatedly until no new reservations happen. */ - private Dop[] makeOpcodesArray() { - int size = insns.size(); - Dop[] result = new Dop[size]; + for (;;) { + int newReservedCount = calculateReservedCount(opcodes); + if (oldReservedCount >= newReservedCount) { + break; + } - for (int i = 0; i < size; i++) { - result[i] = insns.get(i).getOpcode(); - } - - return result; - } - - /** - * Helper for {@link #finishProcessingAndGetList}, which figures - * out how many reserved registers are required and then reserving - * them. It also updates the given {@code opcodes} array so - * as to avoid extra work when constructing the massaged - * instruction list. - * - * @param opcodes {@code non-null;} array of per-instruction - * opcode selections - */ - private void reserveRegisters(Dop[] opcodes) { - int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount; + int reservedDifference = newReservedCount - oldReservedCount; + int size = insns.size(); + for (int i = 0; i < size; i++) { /* - * Call calculateReservedCount() and then perform register - * reservation, repeatedly until no new reservations happen. + * CodeAddress instance identity is used to link + * TargetInsns to their targets, so it is + * inappropriate to make replacements, and they don't + * have registers in any case. Hence, the instanceof + * test below. */ - for (;;) { - int newReservedCount = calculateReservedCount(opcodes); - if (oldReservedCount >= newReservedCount) { - break; - } - - int reservedDifference = newReservedCount - oldReservedCount; - int size = insns.size(); - - for (int i = 0; i < size; i++) { - /* - * CodeAddress instance identity is used to link - * TargetInsns to their targets, so it is - * inappropriate to make replacements, and they don't - * have registers in any case. Hence, the instanceof - * test below. - */ - DalvInsn insn = insns.get(i); - if (!(insn instanceof CodeAddress)) { - /* - * No need to call this.set() since the format and - * other info are the same. - */ - insns.set(i, insn.withRegisterOffset(reservedDifference)); - } - } - - oldReservedCount = newReservedCount; + DalvInsn insn = insns.get(i); + if (!(insn instanceof CodeAddress)) { + /* + * No need to call this.set() since the format and + * other info are the same. + */ + insns.set(i, insn.withRegisterOffset(reservedDifference)); } + } - reservedCount = oldReservedCount; + oldReservedCount = newReservedCount; } - /** - * Helper for {@link #reserveRegisters}, which does one - * pass over the instructions, calculating the number of - * registers that need to be reserved. It also updates the - * {@code opcodes} list to help avoid extra work in future - * register reservation passes. - * - * @param opcodes {@code non-null;} array of per-instruction - * opcode selections - * @return {@code >= 0;} the count of reserved registers + reservedCount = oldReservedCount; + } + + /** + * Helper for {@link #reserveRegisters}, which does one + * pass over the instructions, calculating the number of + * registers that need to be reserved. It also updates the + * {@code opcodes} list to help avoid extra work in future + * register reservation passes. + * + * @param opcodes {@code non-null;} array of per-instruction + * opcode selections + * @return {@code >= 0;} the count of reserved registers + */ + private int calculateReservedCount(Dop[] opcodes) { + int size = insns.size(); + + /* + * Potential new value of reservedCount, which gets updated in the + * following loop. It starts out with the existing reservedCount + * and gets increased if it turns out that additional registers + * need to be reserved. */ - private int calculateReservedCount(Dop[] opcodes) { - int size = insns.size(); + int newReservedCount = reservedCount; + + for (int i = 0; i < size; i++) { + DalvInsn insn = insns.get(i); + Dop originalOpcode = opcodes[i]; + Dop newOpcode = findOpcodeForInsn(insn, originalOpcode); + if (newOpcode == null) { /* - * Potential new value of reservedCount, which gets updated in the - * following loop. It starts out with the existing reservedCount - * and gets increased if it turns out that additional registers - * need to be reserved. + * The instruction will need to be expanded, so find the + * expanded opcode and reserve registers for it. */ - int newReservedCount = reservedCount; - - for (int i = 0; i < size; i++) { - DalvInsn insn = insns.get(i); - Dop originalOpcode = opcodes[i]; - Dop newOpcode = findOpcodeForInsn(insn, originalOpcode); - - if (newOpcode == null) { - /* - * The instruction will need to be expanded, so find the - * expanded opcode and reserve registers for it. - */ - Dop expandedOp = findExpandedOpcodeForInsn(insn); - BitSet compatRegs = expandedOp.getFormat().compatibleRegs(insn); - int reserve = insn.getMinimumRegisterRequirement(compatRegs); - if (reserve > newReservedCount) { - newReservedCount = reserve; - } - } else if (originalOpcode == newOpcode) { - continue; - } - - opcodes[i] = newOpcode; + Dop expandedOp = findExpandedOpcodeForInsn(insn); + BitSet compatRegs = expandedOp.getFormat().compatibleRegs(insn); + int reserve = insn.getMinimumRegisterRequirement(compatRegs); + if (reserve > newReservedCount) { + newReservedCount = reserve; } + } else if (originalOpcode == newOpcode) { + continue; + } - return newReservedCount; + opcodes[i] = newOpcode; } - /** - * Attempts to fit the given instruction into a specific opcode, - * returning the opcode whose format that the instruction fits - * into or {@code null} to indicate that the instruction will need - * to be expanded. This fitting process starts with the given - * opcode as a first "best guess" and then pessimizes from there - * if necessary. - * - * @param insn {@code non-null;} the instruction in question - * @param guess {@code null-ok;} the current guess as to the best - * opcode; {@code null} means that no simple opcode fits - * @return {@code null-ok;} a possibly-different opcode; either a - * {@code non-null} good fit or {@code null} to indicate that no - * simple opcode fits + return newReservedCount; + } + + /** + * Attempts to fit the given instruction into a specific opcode, + * returning the opcode whose format that the instruction fits + * into or {@code null} to indicate that the instruction will need + * to be expanded. This fitting process starts with the given + * opcode as a first "best guess" and then pessimizes from there + * if necessary. + * + * @param insn {@code non-null;} the instruction in question + * @param guess {@code null-ok;} the current guess as to the best + * opcode; {@code null} means that no simple opcode fits + * @return {@code null-ok;} a possibly-different opcode; either a + * {@code non-null} good fit or {@code null} to indicate that no + * simple opcode fits + */ + private Dop findOpcodeForInsn(DalvInsn insn, Dop guess) { + /* + * Note: The initial guess might be null, meaning that an + * earlier call to this method already determined that there + * was no possible simple opcode fit. */ - private Dop findOpcodeForInsn(DalvInsn insn, Dop guess) { + +while (guess != null) { + if (guess.getFormat().isCompatible(insn)) { /* - * Note: The initial guess might be null, meaning that an - * earlier call to this method already determined that there - * was no possible simple opcode fit. + * Don't break out for const_string to generate jumbo version + * when option is enabled. */ - - while (guess != null) { - if (guess.getFormat().isCompatible(insn)) { - /* - * Don't break out for const_string to generate jumbo version - * when option is enabled. - */ - if (!dexOptions.forceJumbo || - guess.getOpcode() != Opcodes.CONST_STRING) { - break; - } - } - - guess = Dops.getNextOrNull(guess, dexOptions); + if (!dexOptions.forceJumbo || guess.getOpcode() != Opcodes.CONST_STRING) { + break; } + } - return guess; + guess = Dops.getNextOrNull(guess, dexOptions); } - /** - * Finds the proper opcode for the given instruction, ignoring - * register constraints. - * - * @param insn {@code non-null;} the instruction in question - * @return {@code non-null;} the opcode that fits - */ - private Dop findExpandedOpcodeForInsn(DalvInsn insn) { - Dop result = findOpcodeForInsn(insn.getLowRegVersion(), insn.getOpcode()); - if (result == null) { - throw new DexException("No expanded opcode for " + insn); - } - return result; + return guess; + } + + /** + * Finds the proper opcode for the given instruction, ignoring + * register constraints. + * + * @param insn {@code non-null;} the instruction in question + * @return {@code non-null;} the opcode that fits + */ + private Dop findExpandedOpcodeForInsn(DalvInsn insn) { + Dop result = findOpcodeForInsn(insn.getLowRegVersion(), insn.getOpcode()); + if (result == null) { + throw new DexException("No expanded opcode for " + insn); } - - /** - * Helper for {@link #finishProcessingAndGetList}, which goes - * through each instruction in the output, making sure its opcode - * can accomodate its arguments. In cases where the opcode is - * unable to do so, this replaces the instruction with a larger - * instruction with identical semantics that <i>will</i> work. - * - * <p>This method may also reserve a number of low-numbered - * registers, renumbering the instructions' original registers, in - * order to have register space available in which to move - * very-high registers when expanding instructions into - * multi-instruction sequences. This expansion is done when no - * simple instruction format can be found for a given instruction that - * is able to accomodate that instruction's registers.</p> - * - * <p>This method ignores issues of branch target size, since - * final addresses aren't known at the point that this method is - * called.</p> - * - * @param opcodes {@code non-null;} array of per-instruction - * opcode selections - */ - private void massageInstructions(Dop[] opcodes) { - if (reservedCount == 0) { - /* - * The easy common case: No registers were reserved, so we - * merely need to replace any instructions whose format - * (and hence whose opcode) changed during the reservation - * pass, but all instructions will stay at their original - * indices, and the instruction list doesn't grow. - */ - int size = insns.size(); - - for (int i = 0; i < size; i++) { - DalvInsn insn = insns.get(i); - Dop originalOpcode = insn.getOpcode(); - Dop currentOpcode = opcodes[i]; - - if (originalOpcode != currentOpcode) { - insns.set(i, insn.withOpcode(currentOpcode)); - } - } - } else { - /* - * The difficult uncommon case: Some instructions have to be - * expanded to deal with high registers. - */ - insns = performExpansion(opcodes); + return result; + } + + /** + * Helper for {@link #finishProcessingAndGetList}, which goes + * through each instruction in the output, making sure its opcode + * can accomodate its arguments. In cases where the opcode is + * unable to do so, this replaces the instruction with a larger + * instruction with identical semantics that <i>will</i> work. + * + * <p>This method may also reserve a number of low-numbered + * registers, renumbering the instructions' original registers, in + * order to have register space available in which to move + * very-high registers when expanding instructions into + * multi-instruction sequences. This expansion is done when no + * simple instruction format can be found for a given instruction that + * is able to accomodate that instruction's registers.</p> + * + * <p>This method ignores issues of branch target size, since + * final addresses aren't known at the point that this method is + * called.</p> + * + * @param opcodes {@code non-null;} array of per-instruction + * opcode selections + */ + private void massageInstructions(Dop[] opcodes) { + if (reservedCount == 0) { + /* + * The easy common case: No registers were reserved, so we + * merely need to replace any instructions whose format + * (and hence whose opcode) changed during the reservation + * pass, but all instructions will stay at their original + * indices, and the instruction list doesn't grow. + */ + int size = insns.size(); + + for (int i = 0; i < size; i++) { + DalvInsn insn = insns.get(i); + Dop originalOpcode = insn.getOpcode(); + Dop currentOpcode = opcodes[i]; + + if (originalOpcode != currentOpcode) { + insns.set(i, insn.withOpcode(currentOpcode)); } + } + } else { + /* + * The difficult uncommon case: Some instructions have to be + * expanded to deal with high registers. + */ + insns = performExpansion(opcodes); } - - /** - * Helper for {@link #massageInstructions}, which constructs a - * replacement list, where each {link DalvInsn} instance that - * couldn't be represented simply (due to register representation - * problems) is expanded into a series of instances that together - * perform the proper function. - * - * @param opcodes {@code non-null;} array of per-instruction - * opcode selections - * @return {@code non-null;} the replacement list - */ - private ArrayList<DalvInsn> performExpansion(Dop[] opcodes) { - int size = insns.size(); - ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2); - - ArrayList<CodeAddress> closelyBoundAddresses = new ArrayList<CodeAddress>(); - - for (int i = 0; i < size; i++) { - DalvInsn insn = insns.get(i); - Dop originalOpcode = insn.getOpcode(); - Dop currentOpcode = opcodes[i]; - DalvInsn prefix; - DalvInsn suffix; - - if (currentOpcode != null) { - // No expansion is necessary. - prefix = null; - suffix = null; - } else { - // Expansion is required. - currentOpcode = findExpandedOpcodeForInsn(insn); - BitSet compatRegs = - currentOpcode.getFormat().compatibleRegs(insn); - prefix = insn.expandedPrefix(compatRegs); - suffix = insn.expandedSuffix(compatRegs); - - // Expand necessary registers to fit the new format - insn = insn.expandedVersion(compatRegs); - } - - if (insn instanceof CodeAddress) { - // If we have a closely bound address, don't add it yet, - // because we need to add it after the prefix for the - // instruction it is bound to. - if (((CodeAddress) insn).getBindsClosely()) { - closelyBoundAddresses.add((CodeAddress)insn); - continue; - } - } - - if (prefix != null) { - result.add(prefix); - } - - // Add any pending closely bound addresses - if (!(insn instanceof ZeroSizeInsn) && closelyBoundAddresses.size() > 0) { - for (CodeAddress codeAddress: closelyBoundAddresses) { - result.add(codeAddress); - } - closelyBoundAddresses.clear(); - } - - if (currentOpcode != originalOpcode) { - insn = insn.withOpcode(currentOpcode); - } - result.add(insn); - - if (suffix != null) { - result.add(suffix); - } + } + + /** + * Helper for {@link #massageInstructions}, which constructs a + * replacement list, where each {link DalvInsn} instance that + * couldn't be represented simply (due to register representation + * problems) is expanded into a series of instances that together + * perform the proper function. + * + * @param opcodes {@code non-null;} array of per-instruction + * opcode selections + * @return {@code non-null;} the replacement list + */ + private ArrayList<DalvInsn> performExpansion(Dop[] opcodes) { + int size = insns.size(); + ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2); + + ArrayList<CodeAddress> closelyBoundAddresses = new ArrayList<CodeAddress>(); + + for (int i = 0; i < size; i++) { + DalvInsn insn = insns.get(i); + Dop originalOpcode = insn.getOpcode(); + Dop currentOpcode = opcodes[i]; + DalvInsn prefix; + DalvInsn suffix; + + if (currentOpcode != null) { + // No expansion is necessary. + prefix = null; + suffix = null; + } else { + // Expansion is required. + currentOpcode = findExpandedOpcodeForInsn(insn); + BitSet compatRegs = currentOpcode.getFormat().compatibleRegs(insn); + prefix = insn.expandedPrefix(compatRegs); + suffix = insn.expandedSuffix(compatRegs); + + // Expand necessary registers to fit the new format + insn = insn.expandedVersion(compatRegs); + } + + if (insn instanceof CodeAddress) { + // If we have a closely bound address, don't add it yet, + // because we need to add it after the prefix for the + // instruction it is bound to. + if (((CodeAddress) insn).getBindsClosely()) { + closelyBoundAddresses.add((CodeAddress) insn); + continue; } + } - return result; - } + if (prefix != null) { + result.add(prefix); + } - /** - * Helper for {@link #finishProcessingAndGetList}, which assigns - * addresses to each instruction, possibly rewriting branches to - * fix ones that wouldn't otherwise be able to reach their - * targets. - */ - private void assignAddressesAndFixBranches() { - for (;;) { - assignAddresses(); - if (!fixBranches()) { - break; - } + // Add any pending closely bound addresses + if (!(insn instanceof ZeroSizeInsn) && closelyBoundAddresses.size() > 0) { + for (CodeAddress codeAddress : closelyBoundAddresses) { + result.add(codeAddress); } - } + closelyBoundAddresses.clear(); + } - /** - * Helper for {@link #assignAddressesAndFixBranches}, which - * assigns an address to each instruction, in order. - */ - private void assignAddresses() { - int address = 0; - int size = insns.size(); - - for (int i = 0; i < size; i++) { - DalvInsn insn = insns.get(i); - insn.setAddress(address); - address += insn.codeSize(); - } + if (currentOpcode != originalOpcode) { + insn = insn.withOpcode(currentOpcode); + } + result.add(insn); + + if (suffix != null) { + result.add(suffix); + } } - /** - * Helper for {@link #assignAddressesAndFixBranches}, which checks - * the branch target size requirement of each branch instruction - * to make sure it fits. For instructions that don't fit, this - * rewrites them to use a {@code goto} of some sort. In the - * case of a conditional branch that doesn't fit, the sense of the - * test is reversed in order to branch around a {@code goto} - * to the original target. - * - * @return whether any branches had to be fixed - */ - private boolean fixBranches() { - int size = insns.size(); - boolean anyFixed = false; - - for (int i = 0; i < size; i++) { - DalvInsn insn = insns.get(i); - if (!(insn instanceof TargetInsn)) { - // This loop only needs to inspect TargetInsns. - continue; - } - - Dop opcode = insn.getOpcode(); - TargetInsn target = (TargetInsn) insn; - - if (opcode.getFormat().branchFits(target)) { - continue; - } - - if (opcode.getFamily() == Opcodes.GOTO) { - // It is a goto; widen it if possible. - opcode = findOpcodeForInsn(insn, opcode); - if (opcode == null) { - /* - * The branch is already maximally large. This should - * only be possible if a method somehow manages to have - * more than 2^31 code units. - */ - throw new UnsupportedOperationException("method too long"); - } - insns.set(i, insn.withOpcode(opcode)); - } else { - /* - * It is a conditional: Reverse its sense, and arrange for - * it to branch around an absolute goto to the original - * branch target. - * - * Note: An invariant of the list being processed is - * that every TargetInsn is followed by a CodeAddress. - * Hence, it is always safe to get the next element - * after a TargetInsn and cast it to CodeAddress, as - * is happening a few lines down. - * - * Also note: Size gets incremented by one here, as we - * have -- in the net -- added one additional element - * to the list, so we increment i to match. The added - * and changed elements will be inspected by a repeat - * call to this method after this invocation returns. - */ - CodeAddress newTarget; - try { - newTarget = (CodeAddress) insns.get(i + 1); - } catch (IndexOutOfBoundsException ex) { - // The TargetInsn / CodeAddress invariant was violated. - throw new IllegalStateException( - "unpaired TargetInsn (dangling)"); - } catch (ClassCastException ex) { - // The TargetInsn / CodeAddress invariant was violated. - throw new IllegalStateException("unpaired TargetInsn"); - } - TargetInsn gotoInsn = - new TargetInsn(Dops.GOTO, target.getPosition(), - RegisterSpecList.EMPTY, target.getTarget()); - insns.set(i, gotoInsn); - insns.add(i, target.withNewTargetAndReversed(newTarget)); - size++; - i++; - } - - anyFixed = true; + return result; + } + + /** + * Helper for {@link #finishProcessingAndGetList}, which assigns + * addresses to each instruction, possibly rewriting branches to + * fix ones that wouldn't otherwise be able to reach their + * targets. + */ + private void assignAddressesAndFixBranches() { + for (;;) { + assignAddresses(); + if (!fixBranches()) { + break; + } + } + } + + /** + * Helper for {@link #assignAddressesAndFixBranches}, which + * assigns an address to each instruction, in order. + */ + private void assignAddresses() { + int address = 0; + int size = insns.size(); + + for (int i = 0; i < size; i++) { + DalvInsn insn = insns.get(i); + insn.setAddress(address); + address += insn.codeSize(); + } + } + + /** + * Helper for {@link #assignAddressesAndFixBranches}, which checks + * the branch target size requirement of each branch instruction + * to make sure it fits. For instructions that don't fit, this + * rewrites them to use a {@code goto} of some sort. In the + * case of a conditional branch that doesn't fit, the sense of the + * test is reversed in order to branch around a {@code goto} + * to the original target. + * + * @return whether any branches had to be fixed + */ + private boolean fixBranches() { + int size = insns.size(); + boolean anyFixed = false; + + for (int i = 0; i < size; i++) { + DalvInsn insn = insns.get(i); + if (!(insn instanceof TargetInsn)) { + // This loop only needs to inspect TargetInsns. + continue; + } + + Dop opcode = insn.getOpcode(); + TargetInsn target = (TargetInsn) insn; + + if (opcode.getFormat().branchFits(target)) { + continue; + } + + if (opcode.getFamily() == Opcodes.GOTO) { + // It is a goto; widen it if possible. + opcode = findOpcodeForInsn(insn, opcode); + if (opcode == null) { + /* + * The branch is already maximally large. This should + * only be possible if a method somehow manages to have + * more than 2^31 code units. + */ + throw new UnsupportedOperationException("method too long"); } - - return anyFixed; + insns.set(i, insn.withOpcode(opcode)); + } else { + /* + * It is a conditional: Reverse its sense, and arrange for + * it to branch around an absolute goto to the original + * branch target. + * + * Note: An invariant of the list being processed is + * that every TargetInsn is followed by a CodeAddress. + * Hence, it is always safe to get the next element + * after a TargetInsn and cast it to CodeAddress, as + * is happening a few lines down. + * + * Also note: Size gets incremented by one here, as we + * have -- in the net -- added one additional element + * to the list, so we increment i to match. The added + * and changed elements will be inspected by a repeat + * call to this method after this invocation returns. + */ + CodeAddress newTarget; + try { + newTarget = (CodeAddress) insns.get(i + 1); + } catch (IndexOutOfBoundsException ex) { + // The TargetInsn / CodeAddress invariant was violated. + throw new IllegalStateException("unpaired TargetInsn (dangling)"); + } catch (ClassCastException ex) { + // The TargetInsn / CodeAddress invariant was violated. + throw new IllegalStateException("unpaired TargetInsn"); + } + TargetInsn gotoInsn = new TargetInsn(Dops.GOTO, target.getPosition(), + RegisterSpecList.EMPTY, target.getTarget()); + insns.set(i, gotoInsn); + insns.add(i, target.withNewTargetAndReversed(newTarget)); + size++; + i++; + } + + anyFixed = true; } + + return anyFixed; + } } diff --git a/dx/src/com/android/jack/dx/dex/code/PositionList.java b/dx/src/com/android/jack/dx/dex/code/PositionList.java index 75432d8..9b4eedb 100644 --- a/dx/src/com/android/jack/dx/dex/code/PositionList.java +++ b/dx/src/com/android/jack/dx/dex/code/PositionList.java @@ -24,169 +24,169 @@ import com.android.jack.dx.util.FixedSizeList; * method to extract an instance out of a {@link DalvInsnList}. */ public final class PositionList extends FixedSizeList { - /** {@code non-null;} empty instance */ - public static final PositionList EMPTY = new PositionList(0); + /** {@code non-null;} empty instance */ + public static final PositionList EMPTY = new PositionList(0); + + /** + * constant for {@link #make} to indicate that no actual position + * information should be returned + */ + public static final int NONE = 1; + + /** + * constant for {@link #make} to indicate that only line number + * transitions should be returned + */ + public static final int LINES = 2; + + /** + * constant for {@link #make} to indicate that only "important" position + * information should be returned. This includes block starts and + * instructions that might throw. + */ + public static final int IMPORTANT = 3; + + /** + * Extracts and returns the source position information out of an + * instruction list. + * + * @param insns {@code non-null;} instructions to convert + * @param howMuch how much information should be included; one of the + * static constants defined by this class + * @return {@code non-null;} the positions list + */ + public static PositionList make(DalvInsnList insns, int howMuch) { + switch (howMuch) { + case NONE: { + return EMPTY; + } + case LINES: + case IMPORTANT: { + // Valid. + break; + } + default: { + throw new IllegalArgumentException("bogus howMuch"); + } + } - /** - * constant for {@link #make} to indicate that no actual position - * information should be returned - */ - public static final int NONE = 1; + SourcePosition noInfo = SourcePosition.NO_INFO; + SourcePosition cur = noInfo; + int sz = insns.size(); + PositionList.Entry[] arr = new PositionList.Entry[sz]; + boolean lastWasTarget = false; + int at = 0; - /** - * constant for {@link #make} to indicate that only line number - * transitions should be returned - */ - public static final int LINES = 2; + for (int i = 0; i < sz; i++) { + DalvInsn insn = insns.get(i); - /** - * constant for {@link #make} to indicate that only "important" position - * information should be returned. This includes block starts and - * instructions that might throw. - */ - public static final int IMPORTANT = 3; + if (insn instanceof CodeAddress) { + lastWasTarget = true;; + continue; + } - /** - * Extracts and returns the source position information out of an - * instruction list. - * - * @param insns {@code non-null;} instructions to convert - * @param howMuch how much information should be included; one of the - * static constants defined by this class - * @return {@code non-null;} the positions list - */ - public static PositionList make(DalvInsnList insns, int howMuch) { - switch (howMuch) { - case NONE: { - return EMPTY; - } - case LINES: - case IMPORTANT: { - // Valid. - break; - } - default: { - throw new IllegalArgumentException("bogus howMuch"); - } - } - - SourcePosition noInfo = SourcePosition.NO_INFO; - SourcePosition cur = noInfo; - int sz = insns.size(); - PositionList.Entry[] arr = new PositionList.Entry[sz]; - boolean lastWasTarget = false; - int at = 0; - - for (int i = 0; i < sz; i++) { - DalvInsn insn = insns.get(i); - - if (insn instanceof CodeAddress) { - lastWasTarget = true;; - continue; - } - - SourcePosition pos = insn.getPosition(); - - if (pos.equals(noInfo) || pos.sameLine(cur)) { - continue; - } - - if ((howMuch == IMPORTANT) && !lastWasTarget) { - continue; - } - - cur = pos; - arr[at] = new PositionList.Entry(insn.getAddress(), pos); - at++; - - lastWasTarget = false; - } - - PositionList result = new PositionList(at); - for (int i = 0; i < at; i++) { - result.set(i, arr[i]); - } - - result.setImmutable(); - return result; + SourcePosition pos = insn.getPosition(); + + if (pos.equals(noInfo) || pos.sameLine(cur)) { + continue; + } + + if ((howMuch == IMPORTANT) && !lastWasTarget) { + continue; + } + + cur = pos; + arr[at] = new PositionList.Entry(insn.getAddress(), pos); + at++; + + lastWasTarget = false; } - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size {@code >= 0;} the size of the list - */ - public PositionList(int size) { - super(size); + PositionList result = new PositionList(at); + for (int i = 0; i < at; i++) { + result.set(i, arr[i]); } + result.setImmutable(); + return result; + } + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size {@code >= 0;} the size of the list + */ + public PositionList(int size) { + super(size); + } + + /** + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index + */ + public Entry get(int n) { + return (Entry) get0(n); + } + + /** + * Sets the entry at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param entry {@code non-null;} the entry to set at {@code n} + */ + public void set(int n, Entry entry) { + set0(n, entry); + } + + /** + * Entry in a position list. + */ + public static class Entry { + /** {@code >= 0;} address of this entry */ + private final int address; + + /** {@code non-null;} corresponding source position information */ + private final SourcePosition position; + /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. + * Constructs an instance. * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index + * @param address {@code >= 0;} address of this entry + * @param position {@code non-null;} corresponding source position information */ - public Entry get(int n) { - return (Entry) get0(n); + public Entry(int address, SourcePosition position) { + if (address < 0) { + throw new IllegalArgumentException("address < 0"); + } + + if (position == null) { + throw new NullPointerException("position == null"); + } + + this.address = address; + this.position = position; } /** - * Sets the entry at the given index. + * Gets the address. * - * @param n {@code >= 0, < size();} which index - * @param entry {@code non-null;} the entry to set at {@code n} + * @return {@code >= 0;} the address */ - public void set(int n, Entry entry) { - set0(n, entry); + public int getAddress() { + return address; } /** - * Entry in a position list. + * Gets the source position information. + * + * @return {@code non-null;} the position information */ - public static class Entry { - /** {@code >= 0;} address of this entry */ - private final int address; - - /** {@code non-null;} corresponding source position information */ - private final SourcePosition position; - - /** - * Constructs an instance. - * - * @param address {@code >= 0;} address of this entry - * @param position {@code non-null;} corresponding source position information - */ - public Entry (int address, SourcePosition position) { - if (address < 0) { - throw new IllegalArgumentException("address < 0"); - } - - if (position == null) { - throw new NullPointerException("position == null"); - } - - this.address = address; - this.position = position; - } - - /** - * Gets the address. - * - * @return {@code >= 0;} the address - */ - public int getAddress() { - return address; - } - - /** - * Gets the source position information. - * - * @return {@code non-null;} the position information - */ - public SourcePosition getPosition() { - return position; - } + public SourcePosition getPosition() { + return position; } + } } diff --git a/dx/src/com/android/jack/dx/dex/code/RopToDop.java b/dx/src/com/android/jack/dx/dex/code/RopToDop.java index 6f64397..5ea7830 100644 --- a/dx/src/com/android/jack/dx/dex/code/RopToDop.java +++ b/dx/src/com/android/jack/dx/dex/code/RopToDop.java @@ -35,553 +35,578 @@ import java.util.HashMap; * {@link Dop} instances. */ public final class RopToDop { - /** {@code non-null;} map from all the common rops to dalvik opcodes */ - private static final HashMap<Rop, Dop> MAP; + /** {@code non-null;} map from all the common rops to dalvik opcodes */ + private static final HashMap<Rop, Dop> MAP; + + /** + * This class is uninstantiable. + */ + private RopToDop() { + // This space intentionally left blank. + } + + /* + * The following comment lists each opcode that should be considered + * the "head" of an opcode chain, in terms of the process of fitting + * an instruction's arguments to an actual opcode. This list is + * automatically generated and may be of use in double-checking the + * manually-generated static initialization code for this class. + * + * TODO(dx team): Make opcode-gen produce useful code in this case instead + * of just a comment. + */ + + // BEGIN(first-opcodes); GENERATED AUTOMATICALLY BY opcode-gen + // Opcodes.NOP + // Opcodes.MOVE + // Opcodes.MOVE_WIDE + // Opcodes.MOVE_OBJECT + // Opcodes.MOVE_RESULT + // Opcodes.MOVE_RESULT_WIDE + // Opcodes.MOVE_RESULT_OBJECT + // Opcodes.MOVE_EXCEPTION + // Opcodes.RETURN_VOID + // Opcodes.RETURN + // Opcodes.RETURN_WIDE + // Opcodes.RETURN_OBJECT + // Opcodes.CONST_4 + // Opcodes.CONST_WIDE_16 + // Opcodes.CONST_STRING + // Opcodes.CONST_CLASS + // Opcodes.MONITOR_ENTER + // Opcodes.MONITOR_EXIT + // Opcodes.CHECK_CAST + // Opcodes.INSTANCE_OF + // Opcodes.ARRAY_LENGTH + // Opcodes.NEW_INSTANCE + // Opcodes.NEW_ARRAY + // Opcodes.FILLED_NEW_ARRAY + // Opcodes.FILL_ARRAY_DATA + // Opcodes.THROW + // Opcodes.GOTO + // Opcodes.PACKED_SWITCH + // Opcodes.SPARSE_SWITCH + // Opcodes.CMPL_FLOAT + // Opcodes.CMPG_FLOAT + // Opcodes.CMPL_DOUBLE + // Opcodes.CMPG_DOUBLE + // Opcodes.CMP_LONG + // Opcodes.IF_EQ + // Opcodes.IF_NE + // Opcodes.IF_LT + // Opcodes.IF_GE + // Opcodes.IF_GT + // Opcodes.IF_LE + // Opcodes.IF_EQZ + // Opcodes.IF_NEZ + // Opcodes.IF_LTZ + // Opcodes.IF_GEZ + // Opcodes.IF_GTZ + // Opcodes.IF_LEZ + // Opcodes.AGET + // Opcodes.AGET_WIDE + // Opcodes.AGET_OBJECT + // Opcodes.AGET_BOOLEAN + // Opcodes.AGET_BYTE + // Opcodes.AGET_CHAR + // Opcodes.AGET_SHORT + // Opcodes.APUT + // Opcodes.APUT_WIDE + // Opcodes.APUT_OBJECT + // Opcodes.APUT_BOOLEAN + // Opcodes.APUT_BYTE + // Opcodes.APUT_CHAR + // Opcodes.APUT_SHORT + // Opcodes.IGET + // Opcodes.IGET_WIDE + // Opcodes.IGET_OBJECT + // Opcodes.IGET_BOOLEAN + // Opcodes.IGET_BYTE + // Opcodes.IGET_CHAR + // Opcodes.IGET_SHORT + // Opcodes.IPUT + // Opcodes.IPUT_WIDE + // Opcodes.IPUT_OBJECT + // Opcodes.IPUT_BOOLEAN + // Opcodes.IPUT_BYTE + // Opcodes.IPUT_CHAR + // Opcodes.IPUT_SHORT + // Opcodes.SGET + // Opcodes.SGET_WIDE + // Opcodes.SGET_OBJECT + // Opcodes.SGET_BOOLEAN + // Opcodes.SGET_BYTE + // Opcodes.SGET_CHAR + // Opcodes.SGET_SHORT + // Opcodes.SPUT + // Opcodes.SPUT_WIDE + // Opcodes.SPUT_OBJECT + // Opcodes.SPUT_BOOLEAN + // Opcodes.SPUT_BYTE + // Opcodes.SPUT_CHAR + // Opcodes.SPUT_SHORT + // Opcodes.INVOKE_VIRTUAL + // Opcodes.INVOKE_SUPER + // Opcodes.INVOKE_DIRECT + // Opcodes.INVOKE_STATIC + // Opcodes.INVOKE_INTERFACE + // Opcodes.NEG_INT + // Opcodes.NOT_INT + // Opcodes.NEG_LONG + // Opcodes.NOT_LONG + // Opcodes.NEG_FLOAT + // Opcodes.NEG_DOUBLE + // Opcodes.INT_TO_LONG + // Opcodes.INT_TO_FLOAT + // Opcodes.INT_TO_DOUBLE + // Opcodes.LONG_TO_INT + // Opcodes.LONG_TO_FLOAT + // Opcodes.LONG_TO_DOUBLE + // Opcodes.FLOAT_TO_INT + // Opcodes.FLOAT_TO_LONG + // Opcodes.FLOAT_TO_DOUBLE + // Opcodes.DOUBLE_TO_INT + // Opcodes.DOUBLE_TO_LONG + // Opcodes.DOUBLE_TO_FLOAT + // Opcodes.INT_TO_BYTE + // Opcodes.INT_TO_CHAR + // Opcodes.INT_TO_SHORT + // Opcodes.ADD_INT_2ADDR + // Opcodes.SUB_INT_2ADDR + // Opcodes.MUL_INT_2ADDR + // Opcodes.DIV_INT_2ADDR + // Opcodes.REM_INT_2ADDR + // Opcodes.AND_INT_2ADDR + // Opcodes.OR_INT_2ADDR + // Opcodes.XOR_INT_2ADDR + // Opcodes.SHL_INT_2ADDR + // Opcodes.SHR_INT_2ADDR + // Opcodes.USHR_INT_2ADDR + // Opcodes.ADD_LONG_2ADDR + // Opcodes.SUB_LONG_2ADDR + // Opcodes.MUL_LONG_2ADDR + // Opcodes.DIV_LONG_2ADDR + // Opcodes.REM_LONG_2ADDR + // Opcodes.AND_LONG_2ADDR + // Opcodes.OR_LONG_2ADDR + // Opcodes.XOR_LONG_2ADDR + // Opcodes.SHL_LONG_2ADDR + // Opcodes.SHR_LONG_2ADDR + // Opcodes.USHR_LONG_2ADDR + // Opcodes.ADD_FLOAT_2ADDR + // Opcodes.SUB_FLOAT_2ADDR + // Opcodes.MUL_FLOAT_2ADDR + // Opcodes.DIV_FLOAT_2ADDR + // Opcodes.REM_FLOAT_2ADDR + // Opcodes.ADD_DOUBLE_2ADDR + // Opcodes.SUB_DOUBLE_2ADDR + // Opcodes.MUL_DOUBLE_2ADDR + // Opcodes.DIV_DOUBLE_2ADDR + // Opcodes.REM_DOUBLE_2ADDR + // Opcodes.ADD_INT_LIT8 + // Opcodes.RSUB_INT_LIT8 + // Opcodes.MUL_INT_LIT8 + // Opcodes.DIV_INT_LIT8 + // Opcodes.REM_INT_LIT8 + // Opcodes.AND_INT_LIT8 + // Opcodes.OR_INT_LIT8 + // Opcodes.XOR_INT_LIT8 + // Opcodes.SHL_INT_LIT8 + // Opcodes.SHR_INT_LIT8 + // Opcodes.USHR_INT_LIT8 + // END(first-opcodes) + + static { + /* + * Note: The choices made here are to pick the optimistically + * smallest Dalvik opcode, and leave it to later processing to + * pessimize. See the automatically-generated comment above + * for reference. + */ + MAP = new HashMap<Rop, Dop>(400); + MAP.put(Rops.NOP, Dops.NOP); + MAP.put(Rops.MOVE_INT, Dops.MOVE); + MAP.put(Rops.MOVE_LONG, Dops.MOVE_WIDE); + MAP.put(Rops.MOVE_FLOAT, Dops.MOVE); + MAP.put(Rops.MOVE_DOUBLE, Dops.MOVE_WIDE); + MAP.put(Rops.MOVE_OBJECT, Dops.MOVE_OBJECT); + MAP.put(Rops.MOVE_PARAM_INT, Dops.MOVE); + MAP.put(Rops.MOVE_PARAM_LONG, Dops.MOVE_WIDE); + MAP.put(Rops.MOVE_PARAM_FLOAT, Dops.MOVE); + MAP.put(Rops.MOVE_PARAM_DOUBLE, Dops.MOVE_WIDE); + MAP.put(Rops.MOVE_PARAM_OBJECT, Dops.MOVE_OBJECT); - /** - * This class is uninstantiable. + /* + * Note: No entry for MOVE_EXCEPTION, since it varies by + * exception type. (That is, there is no unique instance to + * add to the map.) */ - private RopToDop() { - // This space intentionally left blank. - } + +MAP.put(Rops.CONST_INT, Dops.CONST_4); + MAP.put(Rops.CONST_LONG, Dops.CONST_WIDE_16); + MAP.put(Rops.CONST_FLOAT, Dops.CONST_4); + MAP.put(Rops.CONST_DOUBLE, Dops.CONST_WIDE_16); /* - * The following comment lists each opcode that should be considered - * the "head" of an opcode chain, in terms of the process of fitting - * an instruction's arguments to an actual opcode. This list is - * automatically generated and may be of use in double-checking the - * manually-generated static initialization code for this class. - * - * TODO: Make opcode-gen produce useful code in this case instead - * of just a comment. + * Note: No entry for CONST_OBJECT, since it needs to turn + * into either CONST_STRING or CONST_CLASS. + */ + +/* + * TODO(dx team): I think the only case of this is for null, and + * const/4 should cover that. + */ + MAP.put(Rops.CONST_OBJECT_NOTHROW, Dops.CONST_4); + + MAP.put(Rops.GOTO, Dops.GOTO); + MAP.put(Rops.IF_EQZ_INT, Dops.IF_EQZ); + MAP.put(Rops.IF_NEZ_INT, Dops.IF_NEZ); + MAP.put(Rops.IF_LTZ_INT, Dops.IF_LTZ); + MAP.put(Rops.IF_GEZ_INT, Dops.IF_GEZ); + MAP.put(Rops.IF_LEZ_INT, Dops.IF_LEZ); + MAP.put(Rops.IF_GTZ_INT, Dops.IF_GTZ); + MAP.put(Rops.IF_EQZ_OBJECT, Dops.IF_EQZ); + MAP.put(Rops.IF_NEZ_OBJECT, Dops.IF_NEZ); + MAP.put(Rops.IF_EQ_INT, Dops.IF_EQ); + MAP.put(Rops.IF_NE_INT, Dops.IF_NE); + MAP.put(Rops.IF_LT_INT, Dops.IF_LT); + MAP.put(Rops.IF_GE_INT, Dops.IF_GE); + MAP.put(Rops.IF_LE_INT, Dops.IF_LE); + MAP.put(Rops.IF_GT_INT, Dops.IF_GT); + MAP.put(Rops.IF_EQ_OBJECT, Dops.IF_EQ); + MAP.put(Rops.IF_NE_OBJECT, Dops.IF_NE); + MAP.put(Rops.SWITCH, Dops.SPARSE_SWITCH); + MAP.put(Rops.ADD_INT, Dops.ADD_INT_2ADDR); + MAP.put(Rops.ADD_LONG, Dops.ADD_LONG_2ADDR); + MAP.put(Rops.ADD_FLOAT, Dops.ADD_FLOAT_2ADDR); + MAP.put(Rops.ADD_DOUBLE, Dops.ADD_DOUBLE_2ADDR); + MAP.put(Rops.SUB_INT, Dops.SUB_INT_2ADDR); + MAP.put(Rops.SUB_LONG, Dops.SUB_LONG_2ADDR); + MAP.put(Rops.SUB_FLOAT, Dops.SUB_FLOAT_2ADDR); + MAP.put(Rops.SUB_DOUBLE, Dops.SUB_DOUBLE_2ADDR); + MAP.put(Rops.MUL_INT, Dops.MUL_INT_2ADDR); + MAP.put(Rops.MUL_LONG, Dops.MUL_LONG_2ADDR); + MAP.put(Rops.MUL_FLOAT, Dops.MUL_FLOAT_2ADDR); + MAP.put(Rops.MUL_DOUBLE, Dops.MUL_DOUBLE_2ADDR); + MAP.put(Rops.DIV_INT, Dops.DIV_INT_2ADDR); + MAP.put(Rops.DIV_LONG, Dops.DIV_LONG_2ADDR); + MAP.put(Rops.DIV_FLOAT, Dops.DIV_FLOAT_2ADDR); + MAP.put(Rops.DIV_DOUBLE, Dops.DIV_DOUBLE_2ADDR); + MAP.put(Rops.REM_INT, Dops.REM_INT_2ADDR); + MAP.put(Rops.REM_LONG, Dops.REM_LONG_2ADDR); + MAP.put(Rops.REM_FLOAT, Dops.REM_FLOAT_2ADDR); + MAP.put(Rops.REM_DOUBLE, Dops.REM_DOUBLE_2ADDR); + MAP.put(Rops.NEG_INT, Dops.NEG_INT); + MAP.put(Rops.NEG_LONG, Dops.NEG_LONG); + MAP.put(Rops.NEG_FLOAT, Dops.NEG_FLOAT); + MAP.put(Rops.NEG_DOUBLE, Dops.NEG_DOUBLE); + MAP.put(Rops.AND_INT, Dops.AND_INT_2ADDR); + MAP.put(Rops.AND_LONG, Dops.AND_LONG_2ADDR); + MAP.put(Rops.OR_INT, Dops.OR_INT_2ADDR); + MAP.put(Rops.OR_LONG, Dops.OR_LONG_2ADDR); + MAP.put(Rops.XOR_INT, Dops.XOR_INT_2ADDR); + MAP.put(Rops.XOR_LONG, Dops.XOR_LONG_2ADDR); + MAP.put(Rops.SHL_INT, Dops.SHL_INT_2ADDR); + MAP.put(Rops.SHL_LONG, Dops.SHL_LONG_2ADDR); + MAP.put(Rops.SHR_INT, Dops.SHR_INT_2ADDR); + MAP.put(Rops.SHR_LONG, Dops.SHR_LONG_2ADDR); + MAP.put(Rops.USHR_INT, Dops.USHR_INT_2ADDR); + MAP.put(Rops.USHR_LONG, Dops.USHR_LONG_2ADDR); + MAP.put(Rops.NOT_INT, Dops.NOT_INT); + MAP.put(Rops.NOT_LONG, Dops.NOT_LONG); + + MAP.put(Rops.ADD_CONST_INT, Dops.ADD_INT_LIT8); + // Note: No dalvik ops for other types of add_const. + + MAP.put(Rops.SUB_CONST_INT, Dops.RSUB_INT_LIT8); + /* + * Note: No dalvik ops for any type of sub_const; instead + * there's a *reverse* sub (constant - reg) for ints only. + */ + +MAP.put(Rops.MUL_CONST_INT, Dops.MUL_INT_LIT8); + // Note: No dalvik ops for other types of mul_const. + + MAP.put(Rops.DIV_CONST_INT, Dops.DIV_INT_LIT8); + // Note: No dalvik ops for other types of div_const. + + MAP.put(Rops.REM_CONST_INT, Dops.REM_INT_LIT8); + // Note: No dalvik ops for other types of rem_const. + + MAP.put(Rops.AND_CONST_INT, Dops.AND_INT_LIT8); + // Note: No dalvik op for and_const_long. + + MAP.put(Rops.OR_CONST_INT, Dops.OR_INT_LIT8); + // Note: No dalvik op for or_const_long. + + MAP.put(Rops.XOR_CONST_INT, Dops.XOR_INT_LIT8); + // Note: No dalvik op for xor_const_long. + + MAP.put(Rops.SHL_CONST_INT, Dops.SHL_INT_LIT8); + // Note: No dalvik op for shl_const_long. + + MAP.put(Rops.SHR_CONST_INT, Dops.SHR_INT_LIT8); + // Note: No dalvik op for shr_const_long. + + MAP.put(Rops.USHR_CONST_INT, Dops.USHR_INT_LIT8); + // Note: No dalvik op for shr_const_long. + + MAP.put(Rops.CMPL_LONG, Dops.CMP_LONG); + MAP.put(Rops.CMPL_FLOAT, Dops.CMPL_FLOAT); + MAP.put(Rops.CMPL_DOUBLE, Dops.CMPL_DOUBLE); + MAP.put(Rops.CMPG_FLOAT, Dops.CMPG_FLOAT); + MAP.put(Rops.CMPG_DOUBLE, Dops.CMPG_DOUBLE); + MAP.put(Rops.CONV_L2I, Dops.LONG_TO_INT); + MAP.put(Rops.CONV_F2I, Dops.FLOAT_TO_INT); + MAP.put(Rops.CONV_D2I, Dops.DOUBLE_TO_INT); + MAP.put(Rops.CONV_I2L, Dops.INT_TO_LONG); + MAP.put(Rops.CONV_F2L, Dops.FLOAT_TO_LONG); + MAP.put(Rops.CONV_D2L, Dops.DOUBLE_TO_LONG); + MAP.put(Rops.CONV_I2F, Dops.INT_TO_FLOAT); + MAP.put(Rops.CONV_L2F, Dops.LONG_TO_FLOAT); + MAP.put(Rops.CONV_D2F, Dops.DOUBLE_TO_FLOAT); + MAP.put(Rops.CONV_I2D, Dops.INT_TO_DOUBLE); + MAP.put(Rops.CONV_L2D, Dops.LONG_TO_DOUBLE); + MAP.put(Rops.CONV_F2D, Dops.FLOAT_TO_DOUBLE); + MAP.put(Rops.TO_BYTE, Dops.INT_TO_BYTE); + MAP.put(Rops.TO_CHAR, Dops.INT_TO_CHAR); + MAP.put(Rops.TO_SHORT, Dops.INT_TO_SHORT); + MAP.put(Rops.RETURN_VOID, Dops.RETURN_VOID); + MAP.put(Rops.RETURN_INT, Dops.RETURN); + MAP.put(Rops.RETURN_LONG, Dops.RETURN_WIDE); + MAP.put(Rops.RETURN_FLOAT, Dops.RETURN); + MAP.put(Rops.RETURN_DOUBLE, Dops.RETURN_WIDE); + MAP.put(Rops.RETURN_OBJECT, Dops.RETURN_OBJECT); + MAP.put(Rops.ARRAY_LENGTH, Dops.ARRAY_LENGTH); + MAP.put(Rops.THROW, Dops.THROW); + MAP.put(Rops.MONITOR_ENTER, Dops.MONITOR_ENTER); + MAP.put(Rops.MONITOR_EXIT, Dops.MONITOR_EXIT); + MAP.put(Rops.AGET_INT, Dops.AGET); + MAP.put(Rops.AGET_LONG, Dops.AGET_WIDE); + MAP.put(Rops.AGET_FLOAT, Dops.AGET); + MAP.put(Rops.AGET_DOUBLE, Dops.AGET_WIDE); + MAP.put(Rops.AGET_OBJECT, Dops.AGET_OBJECT); + MAP.put(Rops.AGET_BOOLEAN, Dops.AGET_BOOLEAN); + MAP.put(Rops.AGET_BYTE, Dops.AGET_BYTE); + MAP.put(Rops.AGET_CHAR, Dops.AGET_CHAR); + MAP.put(Rops.AGET_SHORT, Dops.AGET_SHORT); + MAP.put(Rops.APUT_INT, Dops.APUT); + MAP.put(Rops.APUT_LONG, Dops.APUT_WIDE); + MAP.put(Rops.APUT_FLOAT, Dops.APUT); + MAP.put(Rops.APUT_DOUBLE, Dops.APUT_WIDE); + MAP.put(Rops.APUT_OBJECT, Dops.APUT_OBJECT); + MAP.put(Rops.APUT_BOOLEAN, Dops.APUT_BOOLEAN); + MAP.put(Rops.APUT_BYTE, Dops.APUT_BYTE); + MAP.put(Rops.APUT_CHAR, Dops.APUT_CHAR); + MAP.put(Rops.APUT_SHORT, Dops.APUT_SHORT); + MAP.put(Rops.NEW_INSTANCE, Dops.NEW_INSTANCE); + MAP.put(Rops.CHECK_CAST, Dops.CHECK_CAST); + MAP.put(Rops.INSTANCE_OF, Dops.INSTANCE_OF); + + MAP.put(Rops.GET_FIELD_LONG, Dops.IGET_WIDE); + MAP.put(Rops.GET_FIELD_FLOAT, Dops.IGET); + MAP.put(Rops.GET_FIELD_DOUBLE, Dops.IGET_WIDE); + MAP.put(Rops.GET_FIELD_OBJECT, Dops.IGET_OBJECT); + /* + * Note: No map entries for get_field_* for non-long integral types, + * since they need to be handled specially (see dopFor() below). + */ + +MAP.put(Rops.GET_STATIC_LONG, Dops.SGET_WIDE); + MAP.put(Rops.GET_STATIC_FLOAT, Dops.SGET); + MAP.put(Rops.GET_STATIC_DOUBLE, Dops.SGET_WIDE); + MAP.put(Rops.GET_STATIC_OBJECT, Dops.SGET_OBJECT); + /* + * Note: No map entries for get_static* for non-long integral types, + * since they need to be handled specially (see dopFor() below). + */ + +MAP.put(Rops.PUT_FIELD_LONG, Dops.IPUT_WIDE); + MAP.put(Rops.PUT_FIELD_FLOAT, Dops.IPUT); + MAP.put(Rops.PUT_FIELD_DOUBLE, Dops.IPUT_WIDE); + MAP.put(Rops.PUT_FIELD_OBJECT, Dops.IPUT_OBJECT); + /* + * Note: No map entries for put_field_* for non-long integral types, + * since they need to be handled specially (see dopFor() below). + */ + +MAP.put(Rops.PUT_STATIC_LONG, Dops.SPUT_WIDE); + MAP.put(Rops.PUT_STATIC_FLOAT, Dops.SPUT); + MAP.put(Rops.PUT_STATIC_DOUBLE, Dops.SPUT_WIDE); + MAP.put(Rops.PUT_STATIC_OBJECT, Dops.SPUT_OBJECT); + /* + * Note: No map entries for put_static* for non-long integral types, + * since they need to be handled specially (see dopFor() below). */ - // BEGIN(first-opcodes); GENERATED AUTOMATICALLY BY opcode-gen - // Opcodes.NOP - // Opcodes.MOVE - // Opcodes.MOVE_WIDE - // Opcodes.MOVE_OBJECT - // Opcodes.MOVE_RESULT - // Opcodes.MOVE_RESULT_WIDE - // Opcodes.MOVE_RESULT_OBJECT - // Opcodes.MOVE_EXCEPTION - // Opcodes.RETURN_VOID - // Opcodes.RETURN - // Opcodes.RETURN_WIDE - // Opcodes.RETURN_OBJECT - // Opcodes.CONST_4 - // Opcodes.CONST_WIDE_16 - // Opcodes.CONST_STRING - // Opcodes.CONST_CLASS - // Opcodes.MONITOR_ENTER - // Opcodes.MONITOR_EXIT - // Opcodes.CHECK_CAST - // Opcodes.INSTANCE_OF - // Opcodes.ARRAY_LENGTH - // Opcodes.NEW_INSTANCE - // Opcodes.NEW_ARRAY - // Opcodes.FILLED_NEW_ARRAY - // Opcodes.FILL_ARRAY_DATA - // Opcodes.THROW - // Opcodes.GOTO - // Opcodes.PACKED_SWITCH - // Opcodes.SPARSE_SWITCH - // Opcodes.CMPL_FLOAT - // Opcodes.CMPG_FLOAT - // Opcodes.CMPL_DOUBLE - // Opcodes.CMPG_DOUBLE - // Opcodes.CMP_LONG - // Opcodes.IF_EQ - // Opcodes.IF_NE - // Opcodes.IF_LT - // Opcodes.IF_GE - // Opcodes.IF_GT - // Opcodes.IF_LE - // Opcodes.IF_EQZ - // Opcodes.IF_NEZ - // Opcodes.IF_LTZ - // Opcodes.IF_GEZ - // Opcodes.IF_GTZ - // Opcodes.IF_LEZ - // Opcodes.AGET - // Opcodes.AGET_WIDE - // Opcodes.AGET_OBJECT - // Opcodes.AGET_BOOLEAN - // Opcodes.AGET_BYTE - // Opcodes.AGET_CHAR - // Opcodes.AGET_SHORT - // Opcodes.APUT - // Opcodes.APUT_WIDE - // Opcodes.APUT_OBJECT - // Opcodes.APUT_BOOLEAN - // Opcodes.APUT_BYTE - // Opcodes.APUT_CHAR - // Opcodes.APUT_SHORT - // Opcodes.IGET - // Opcodes.IGET_WIDE - // Opcodes.IGET_OBJECT - // Opcodes.IGET_BOOLEAN - // Opcodes.IGET_BYTE - // Opcodes.IGET_CHAR - // Opcodes.IGET_SHORT - // Opcodes.IPUT - // Opcodes.IPUT_WIDE - // Opcodes.IPUT_OBJECT - // Opcodes.IPUT_BOOLEAN - // Opcodes.IPUT_BYTE - // Opcodes.IPUT_CHAR - // Opcodes.IPUT_SHORT - // Opcodes.SGET - // Opcodes.SGET_WIDE - // Opcodes.SGET_OBJECT - // Opcodes.SGET_BOOLEAN - // Opcodes.SGET_BYTE - // Opcodes.SGET_CHAR - // Opcodes.SGET_SHORT - // Opcodes.SPUT - // Opcodes.SPUT_WIDE - // Opcodes.SPUT_OBJECT - // Opcodes.SPUT_BOOLEAN - // Opcodes.SPUT_BYTE - // Opcodes.SPUT_CHAR - // Opcodes.SPUT_SHORT - // Opcodes.INVOKE_VIRTUAL - // Opcodes.INVOKE_SUPER - // Opcodes.INVOKE_DIRECT - // Opcodes.INVOKE_STATIC - // Opcodes.INVOKE_INTERFACE - // Opcodes.NEG_INT - // Opcodes.NOT_INT - // Opcodes.NEG_LONG - // Opcodes.NOT_LONG - // Opcodes.NEG_FLOAT - // Opcodes.NEG_DOUBLE - // Opcodes.INT_TO_LONG - // Opcodes.INT_TO_FLOAT - // Opcodes.INT_TO_DOUBLE - // Opcodes.LONG_TO_INT - // Opcodes.LONG_TO_FLOAT - // Opcodes.LONG_TO_DOUBLE - // Opcodes.FLOAT_TO_INT - // Opcodes.FLOAT_TO_LONG - // Opcodes.FLOAT_TO_DOUBLE - // Opcodes.DOUBLE_TO_INT - // Opcodes.DOUBLE_TO_LONG - // Opcodes.DOUBLE_TO_FLOAT - // Opcodes.INT_TO_BYTE - // Opcodes.INT_TO_CHAR - // Opcodes.INT_TO_SHORT - // Opcodes.ADD_INT_2ADDR - // Opcodes.SUB_INT_2ADDR - // Opcodes.MUL_INT_2ADDR - // Opcodes.DIV_INT_2ADDR - // Opcodes.REM_INT_2ADDR - // Opcodes.AND_INT_2ADDR - // Opcodes.OR_INT_2ADDR - // Opcodes.XOR_INT_2ADDR - // Opcodes.SHL_INT_2ADDR - // Opcodes.SHR_INT_2ADDR - // Opcodes.USHR_INT_2ADDR - // Opcodes.ADD_LONG_2ADDR - // Opcodes.SUB_LONG_2ADDR - // Opcodes.MUL_LONG_2ADDR - // Opcodes.DIV_LONG_2ADDR - // Opcodes.REM_LONG_2ADDR - // Opcodes.AND_LONG_2ADDR - // Opcodes.OR_LONG_2ADDR - // Opcodes.XOR_LONG_2ADDR - // Opcodes.SHL_LONG_2ADDR - // Opcodes.SHR_LONG_2ADDR - // Opcodes.USHR_LONG_2ADDR - // Opcodes.ADD_FLOAT_2ADDR - // Opcodes.SUB_FLOAT_2ADDR - // Opcodes.MUL_FLOAT_2ADDR - // Opcodes.DIV_FLOAT_2ADDR - // Opcodes.REM_FLOAT_2ADDR - // Opcodes.ADD_DOUBLE_2ADDR - // Opcodes.SUB_DOUBLE_2ADDR - // Opcodes.MUL_DOUBLE_2ADDR - // Opcodes.DIV_DOUBLE_2ADDR - // Opcodes.REM_DOUBLE_2ADDR - // Opcodes.ADD_INT_LIT8 - // Opcodes.RSUB_INT_LIT8 - // Opcodes.MUL_INT_LIT8 - // Opcodes.DIV_INT_LIT8 - // Opcodes.REM_INT_LIT8 - // Opcodes.AND_INT_LIT8 - // Opcodes.OR_INT_LIT8 - // Opcodes.XOR_INT_LIT8 - // Opcodes.SHL_INT_LIT8 - // Opcodes.SHR_INT_LIT8 - // Opcodes.USHR_INT_LIT8 - // END(first-opcodes) - - static { - /* - * Note: The choices made here are to pick the optimistically - * smallest Dalvik opcode, and leave it to later processing to - * pessimize. See the automatically-generated comment above - * for reference. - */ - MAP = new HashMap<Rop, Dop>(400); - MAP.put(Rops.NOP, Dops.NOP); - MAP.put(Rops.MOVE_INT, Dops.MOVE); - MAP.put(Rops.MOVE_LONG, Dops.MOVE_WIDE); - MAP.put(Rops.MOVE_FLOAT, Dops.MOVE); - MAP.put(Rops.MOVE_DOUBLE, Dops.MOVE_WIDE); - MAP.put(Rops.MOVE_OBJECT, Dops.MOVE_OBJECT); - MAP.put(Rops.MOVE_PARAM_INT, Dops.MOVE); - MAP.put(Rops.MOVE_PARAM_LONG, Dops.MOVE_WIDE); - MAP.put(Rops.MOVE_PARAM_FLOAT, Dops.MOVE); - MAP.put(Rops.MOVE_PARAM_DOUBLE, Dops.MOVE_WIDE); - MAP.put(Rops.MOVE_PARAM_OBJECT, Dops.MOVE_OBJECT); - - /* - * Note: No entry for MOVE_EXCEPTION, since it varies by - * exception type. (That is, there is no unique instance to - * add to the map.) - */ - - MAP.put(Rops.CONST_INT, Dops.CONST_4); - MAP.put(Rops.CONST_LONG, Dops.CONST_WIDE_16); - MAP.put(Rops.CONST_FLOAT, Dops.CONST_4); - MAP.put(Rops.CONST_DOUBLE, Dops.CONST_WIDE_16); - - /* - * Note: No entry for CONST_OBJECT, since it needs to turn - * into either CONST_STRING or CONST_CLASS. - */ - - /* - * TODO: I think the only case of this is for null, and - * const/4 should cover that. - */ - MAP.put(Rops.CONST_OBJECT_NOTHROW, Dops.CONST_4); - - MAP.put(Rops.GOTO, Dops.GOTO); - MAP.put(Rops.IF_EQZ_INT, Dops.IF_EQZ); - MAP.put(Rops.IF_NEZ_INT, Dops.IF_NEZ); - MAP.put(Rops.IF_LTZ_INT, Dops.IF_LTZ); - MAP.put(Rops.IF_GEZ_INT, Dops.IF_GEZ); - MAP.put(Rops.IF_LEZ_INT, Dops.IF_LEZ); - MAP.put(Rops.IF_GTZ_INT, Dops.IF_GTZ); - MAP.put(Rops.IF_EQZ_OBJECT, Dops.IF_EQZ); - MAP.put(Rops.IF_NEZ_OBJECT, Dops.IF_NEZ); - MAP.put(Rops.IF_EQ_INT, Dops.IF_EQ); - MAP.put(Rops.IF_NE_INT, Dops.IF_NE); - MAP.put(Rops.IF_LT_INT, Dops.IF_LT); - MAP.put(Rops.IF_GE_INT, Dops.IF_GE); - MAP.put(Rops.IF_LE_INT, Dops.IF_LE); - MAP.put(Rops.IF_GT_INT, Dops.IF_GT); - MAP.put(Rops.IF_EQ_OBJECT, Dops.IF_EQ); - MAP.put(Rops.IF_NE_OBJECT, Dops.IF_NE); - MAP.put(Rops.SWITCH, Dops.SPARSE_SWITCH); - MAP.put(Rops.ADD_INT, Dops.ADD_INT_2ADDR); - MAP.put(Rops.ADD_LONG, Dops.ADD_LONG_2ADDR); - MAP.put(Rops.ADD_FLOAT, Dops.ADD_FLOAT_2ADDR); - MAP.put(Rops.ADD_DOUBLE, Dops.ADD_DOUBLE_2ADDR); - MAP.put(Rops.SUB_INT, Dops.SUB_INT_2ADDR); - MAP.put(Rops.SUB_LONG, Dops.SUB_LONG_2ADDR); - MAP.put(Rops.SUB_FLOAT, Dops.SUB_FLOAT_2ADDR); - MAP.put(Rops.SUB_DOUBLE, Dops.SUB_DOUBLE_2ADDR); - MAP.put(Rops.MUL_INT, Dops.MUL_INT_2ADDR); - MAP.put(Rops.MUL_LONG, Dops.MUL_LONG_2ADDR); - MAP.put(Rops.MUL_FLOAT, Dops.MUL_FLOAT_2ADDR); - MAP.put(Rops.MUL_DOUBLE, Dops.MUL_DOUBLE_2ADDR); - MAP.put(Rops.DIV_INT, Dops.DIV_INT_2ADDR); - MAP.put(Rops.DIV_LONG, Dops.DIV_LONG_2ADDR); - MAP.put(Rops.DIV_FLOAT, Dops.DIV_FLOAT_2ADDR); - MAP.put(Rops.DIV_DOUBLE, Dops.DIV_DOUBLE_2ADDR); - MAP.put(Rops.REM_INT, Dops.REM_INT_2ADDR); - MAP.put(Rops.REM_LONG, Dops.REM_LONG_2ADDR); - MAP.put(Rops.REM_FLOAT, Dops.REM_FLOAT_2ADDR); - MAP.put(Rops.REM_DOUBLE, Dops.REM_DOUBLE_2ADDR); - MAP.put(Rops.NEG_INT, Dops.NEG_INT); - MAP.put(Rops.NEG_LONG, Dops.NEG_LONG); - MAP.put(Rops.NEG_FLOAT, Dops.NEG_FLOAT); - MAP.put(Rops.NEG_DOUBLE, Dops.NEG_DOUBLE); - MAP.put(Rops.AND_INT, Dops.AND_INT_2ADDR); - MAP.put(Rops.AND_LONG, Dops.AND_LONG_2ADDR); - MAP.put(Rops.OR_INT, Dops.OR_INT_2ADDR); - MAP.put(Rops.OR_LONG, Dops.OR_LONG_2ADDR); - MAP.put(Rops.XOR_INT, Dops.XOR_INT_2ADDR); - MAP.put(Rops.XOR_LONG, Dops.XOR_LONG_2ADDR); - MAP.put(Rops.SHL_INT, Dops.SHL_INT_2ADDR); - MAP.put(Rops.SHL_LONG, Dops.SHL_LONG_2ADDR); - MAP.put(Rops.SHR_INT, Dops.SHR_INT_2ADDR); - MAP.put(Rops.SHR_LONG, Dops.SHR_LONG_2ADDR); - MAP.put(Rops.USHR_INT, Dops.USHR_INT_2ADDR); - MAP.put(Rops.USHR_LONG, Dops.USHR_LONG_2ADDR); - MAP.put(Rops.NOT_INT, Dops.NOT_INT); - MAP.put(Rops.NOT_LONG, Dops.NOT_LONG); - - MAP.put(Rops.ADD_CONST_INT, Dops.ADD_INT_LIT8); - // Note: No dalvik ops for other types of add_const. - - MAP.put(Rops.SUB_CONST_INT, Dops.RSUB_INT_LIT8); - /* - * Note: No dalvik ops for any type of sub_const; instead - * there's a *reverse* sub (constant - reg) for ints only. - */ - - MAP.put(Rops.MUL_CONST_INT, Dops.MUL_INT_LIT8); - // Note: No dalvik ops for other types of mul_const. - - MAP.put(Rops.DIV_CONST_INT, Dops.DIV_INT_LIT8); - // Note: No dalvik ops for other types of div_const. - - MAP.put(Rops.REM_CONST_INT, Dops.REM_INT_LIT8); - // Note: No dalvik ops for other types of rem_const. - - MAP.put(Rops.AND_CONST_INT, Dops.AND_INT_LIT8); - // Note: No dalvik op for and_const_long. - - MAP.put(Rops.OR_CONST_INT, Dops.OR_INT_LIT8); - // Note: No dalvik op for or_const_long. - - MAP.put(Rops.XOR_CONST_INT, Dops.XOR_INT_LIT8); - // Note: No dalvik op for xor_const_long. - - MAP.put(Rops.SHL_CONST_INT, Dops.SHL_INT_LIT8); - // Note: No dalvik op for shl_const_long. - - MAP.put(Rops.SHR_CONST_INT, Dops.SHR_INT_LIT8); - // Note: No dalvik op for shr_const_long. - - MAP.put(Rops.USHR_CONST_INT, Dops.USHR_INT_LIT8); - // Note: No dalvik op for shr_const_long. - - MAP.put(Rops.CMPL_LONG, Dops.CMP_LONG); - MAP.put(Rops.CMPL_FLOAT, Dops.CMPL_FLOAT); - MAP.put(Rops.CMPL_DOUBLE, Dops.CMPL_DOUBLE); - MAP.put(Rops.CMPG_FLOAT, Dops.CMPG_FLOAT); - MAP.put(Rops.CMPG_DOUBLE, Dops.CMPG_DOUBLE); - MAP.put(Rops.CONV_L2I, Dops.LONG_TO_INT); - MAP.put(Rops.CONV_F2I, Dops.FLOAT_TO_INT); - MAP.put(Rops.CONV_D2I, Dops.DOUBLE_TO_INT); - MAP.put(Rops.CONV_I2L, Dops.INT_TO_LONG); - MAP.put(Rops.CONV_F2L, Dops.FLOAT_TO_LONG); - MAP.put(Rops.CONV_D2L, Dops.DOUBLE_TO_LONG); - MAP.put(Rops.CONV_I2F, Dops.INT_TO_FLOAT); - MAP.put(Rops.CONV_L2F, Dops.LONG_TO_FLOAT); - MAP.put(Rops.CONV_D2F, Dops.DOUBLE_TO_FLOAT); - MAP.put(Rops.CONV_I2D, Dops.INT_TO_DOUBLE); - MAP.put(Rops.CONV_L2D, Dops.LONG_TO_DOUBLE); - MAP.put(Rops.CONV_F2D, Dops.FLOAT_TO_DOUBLE); - MAP.put(Rops.TO_BYTE, Dops.INT_TO_BYTE); - MAP.put(Rops.TO_CHAR, Dops.INT_TO_CHAR); - MAP.put(Rops.TO_SHORT, Dops.INT_TO_SHORT); - MAP.put(Rops.RETURN_VOID, Dops.RETURN_VOID); - MAP.put(Rops.RETURN_INT, Dops.RETURN); - MAP.put(Rops.RETURN_LONG, Dops.RETURN_WIDE); - MAP.put(Rops.RETURN_FLOAT, Dops.RETURN); - MAP.put(Rops.RETURN_DOUBLE, Dops.RETURN_WIDE); - MAP.put(Rops.RETURN_OBJECT, Dops.RETURN_OBJECT); - MAP.put(Rops.ARRAY_LENGTH, Dops.ARRAY_LENGTH); - MAP.put(Rops.THROW, Dops.THROW); - MAP.put(Rops.MONITOR_ENTER, Dops.MONITOR_ENTER); - MAP.put(Rops.MONITOR_EXIT, Dops.MONITOR_EXIT); - MAP.put(Rops.AGET_INT, Dops.AGET); - MAP.put(Rops.AGET_LONG, Dops.AGET_WIDE); - MAP.put(Rops.AGET_FLOAT, Dops.AGET); - MAP.put(Rops.AGET_DOUBLE, Dops.AGET_WIDE); - MAP.put(Rops.AGET_OBJECT, Dops.AGET_OBJECT); - MAP.put(Rops.AGET_BOOLEAN, Dops.AGET_BOOLEAN); - MAP.put(Rops.AGET_BYTE, Dops.AGET_BYTE); - MAP.put(Rops.AGET_CHAR, Dops.AGET_CHAR); - MAP.put(Rops.AGET_SHORT, Dops.AGET_SHORT); - MAP.put(Rops.APUT_INT, Dops.APUT); - MAP.put(Rops.APUT_LONG, Dops.APUT_WIDE); - MAP.put(Rops.APUT_FLOAT, Dops.APUT); - MAP.put(Rops.APUT_DOUBLE, Dops.APUT_WIDE); - MAP.put(Rops.APUT_OBJECT, Dops.APUT_OBJECT); - MAP.put(Rops.APUT_BOOLEAN, Dops.APUT_BOOLEAN); - MAP.put(Rops.APUT_BYTE, Dops.APUT_BYTE); - MAP.put(Rops.APUT_CHAR, Dops.APUT_CHAR); - MAP.put(Rops.APUT_SHORT, Dops.APUT_SHORT); - MAP.put(Rops.NEW_INSTANCE, Dops.NEW_INSTANCE); - MAP.put(Rops.CHECK_CAST, Dops.CHECK_CAST); - MAP.put(Rops.INSTANCE_OF, Dops.INSTANCE_OF); - - MAP.put(Rops.GET_FIELD_LONG, Dops.IGET_WIDE); - MAP.put(Rops.GET_FIELD_FLOAT, Dops.IGET); - MAP.put(Rops.GET_FIELD_DOUBLE, Dops.IGET_WIDE); - MAP.put(Rops.GET_FIELD_OBJECT, Dops.IGET_OBJECT); - /* - * Note: No map entries for get_field_* for non-long integral types, - * since they need to be handled specially (see dopFor() below). - */ - - MAP.put(Rops.GET_STATIC_LONG, Dops.SGET_WIDE); - MAP.put(Rops.GET_STATIC_FLOAT, Dops.SGET); - MAP.put(Rops.GET_STATIC_DOUBLE, Dops.SGET_WIDE); - MAP.put(Rops.GET_STATIC_OBJECT, Dops.SGET_OBJECT); - /* - * Note: No map entries for get_static* for non-long integral types, - * since they need to be handled specially (see dopFor() below). - */ - - MAP.put(Rops.PUT_FIELD_LONG, Dops.IPUT_WIDE); - MAP.put(Rops.PUT_FIELD_FLOAT, Dops.IPUT); - MAP.put(Rops.PUT_FIELD_DOUBLE, Dops.IPUT_WIDE); - MAP.put(Rops.PUT_FIELD_OBJECT, Dops.IPUT_OBJECT); - /* - * Note: No map entries for put_field_* for non-long integral types, - * since they need to be handled specially (see dopFor() below). - */ - - MAP.put(Rops.PUT_STATIC_LONG, Dops.SPUT_WIDE); - MAP.put(Rops.PUT_STATIC_FLOAT, Dops.SPUT); - MAP.put(Rops.PUT_STATIC_DOUBLE, Dops.SPUT_WIDE); - MAP.put(Rops.PUT_STATIC_OBJECT, Dops.SPUT_OBJECT); - /* - * Note: No map entries for put_static* for non-long integral types, - * since they need to be handled specially (see dopFor() below). - */ - - /* - * Note: No map entries for invoke*, new_array, and - * filled_new_array, since they need to be handled specially - * (see dopFor() below). - */ + /* + * Note: No map entries for invoke*, new_array, and + * filled_new_array, since they need to be handled specially + * (see dopFor() below). + */ + } + + /** + * Returns the dalvik opcode appropriate for the given register-based + * instruction. + * + * @param insn {@code non-null;} the original instruction + * @return the corresponding dalvik opcode; one of the constants in + * {@link Dops} + */ + public static Dop dopFor(Insn insn) { + Rop rop = insn.getOpcode(); + + /* + * First, just try looking up the rop in the MAP of easy + * cases. + */ + Dop result = MAP.get(rop); + if (result != null) { + return result; } - /** - * Returns the dalvik opcode appropriate for the given register-based - * instruction. + /* + * There was no easy case for the rop, so look up the opcode, and + * do something special for each: * - * @param insn {@code non-null;} the original instruction - * @return the corresponding dalvik opcode; one of the constants in - * {@link Dops} + * The move_exception, new_array, filled_new_array, and + * invoke* opcodes won't be found in MAP, since they'll each + * have different source and/or result register types / lists. + * + * The get* and put* opcodes for (non-long) integral types + * aren't in the map, since the type signatures aren't + * sufficient to distinguish between the types (the salient + * source or result will always be just "int"). + * + * And const instruction need to distinguish between strings and + * classes. */ - public static Dop dopFor(Insn insn) { - Rop rop = insn.getOpcode(); - - /* - * First, just try looking up the rop in the MAP of easy - * cases. - */ - Dop result = MAP.get(rop); - if (result != null) { - return result; - } - - /* - * There was no easy case for the rop, so look up the opcode, and - * do something special for each: - * - * The move_exception, new_array, filled_new_array, and - * invoke* opcodes won't be found in MAP, since they'll each - * have different source and/or result register types / lists. - * - * The get* and put* opcodes for (non-long) integral types - * aren't in the map, since the type signatures aren't - * sufficient to distinguish between the types (the salient - * source or result will always be just "int"). - * - * And const instruction need to distinguish between strings and - * classes. - */ - - switch (rop.getOpcode()) { - case RegOps.MOVE_EXCEPTION: return Dops.MOVE_EXCEPTION; - case RegOps.INVOKE_STATIC: return Dops.INVOKE_STATIC; - case RegOps.INVOKE_VIRTUAL: return Dops.INVOKE_VIRTUAL; - case RegOps.INVOKE_SUPER: return Dops.INVOKE_SUPER; - case RegOps.INVOKE_DIRECT: return Dops.INVOKE_DIRECT; - case RegOps.INVOKE_INTERFACE: return Dops.INVOKE_INTERFACE; - case RegOps.NEW_ARRAY: return Dops.NEW_ARRAY; - case RegOps.FILLED_NEW_ARRAY: return Dops.FILLED_NEW_ARRAY; - case RegOps.FILL_ARRAY_DATA: return Dops.FILL_ARRAY_DATA; - case RegOps.MOVE_RESULT: { - RegisterSpec resultReg = insn.getResult(); - - if (resultReg == null) { - return Dops.NOP; - } else { - switch (resultReg.getBasicType()) { - case Type.BT_INT: - case Type.BT_FLOAT: - case Type.BT_BOOLEAN: - case Type.BT_BYTE: - case Type.BT_CHAR: - case Type.BT_SHORT: - return Dops.MOVE_RESULT; - case Type.BT_LONG: - case Type.BT_DOUBLE: - return Dops.MOVE_RESULT_WIDE; - case Type.BT_OBJECT: - return Dops.MOVE_RESULT_OBJECT; - default: { - throw new RuntimeException("Unexpected basic type"); - } - } - } - } - case RegOps.GET_FIELD: { - CstFieldRef ref = - (CstFieldRef) ((ThrowingCstInsn) insn).getConstant(); - int basicType = ref.getBasicType(); - switch (basicType) { - case Type.BT_BOOLEAN: return Dops.IGET_BOOLEAN; - case Type.BT_BYTE: return Dops.IGET_BYTE; - case Type.BT_CHAR: return Dops.IGET_CHAR; - case Type.BT_SHORT: return Dops.IGET_SHORT; - case Type.BT_INT: return Dops.IGET; - } - break; - } - case RegOps.PUT_FIELD: { - CstFieldRef ref = - (CstFieldRef) ((ThrowingCstInsn) insn).getConstant(); - int basicType = ref.getBasicType(); - switch (basicType) { - case Type.BT_BOOLEAN: return Dops.IPUT_BOOLEAN; - case Type.BT_BYTE: return Dops.IPUT_BYTE; - case Type.BT_CHAR: return Dops.IPUT_CHAR; - case Type.BT_SHORT: return Dops.IPUT_SHORT; - case Type.BT_INT: return Dops.IPUT; - } - break; - } - case RegOps.GET_STATIC: { - CstFieldRef ref = - (CstFieldRef) ((ThrowingCstInsn) insn).getConstant(); - int basicType = ref.getBasicType(); - switch (basicType) { - case Type.BT_BOOLEAN: return Dops.SGET_BOOLEAN; - case Type.BT_BYTE: return Dops.SGET_BYTE; - case Type.BT_CHAR: return Dops.SGET_CHAR; - case Type.BT_SHORT: return Dops.SGET_SHORT; - case Type.BT_INT: return Dops.SGET; - } - break; - } - case RegOps.PUT_STATIC: { - CstFieldRef ref = - (CstFieldRef) ((ThrowingCstInsn) insn).getConstant(); - int basicType = ref.getBasicType(); - switch (basicType) { - case Type.BT_BOOLEAN: return Dops.SPUT_BOOLEAN; - case Type.BT_BYTE: return Dops.SPUT_BYTE; - case Type.BT_CHAR: return Dops.SPUT_CHAR; - case Type.BT_SHORT: return Dops.SPUT_SHORT; - case Type.BT_INT: return Dops.SPUT; - } - break; - } - case RegOps.CONST: { - Constant cst = ((ThrowingCstInsn) insn).getConstant(); - if (cst instanceof CstType) { - return Dops.CONST_CLASS; - } else if (cst instanceof CstString) { - return Dops.CONST_STRING; - } - break; +switch (rop.getOpcode()) { + case RegOps.MOVE_EXCEPTION: + return Dops.MOVE_EXCEPTION; + case RegOps.INVOKE_STATIC: + return Dops.INVOKE_STATIC; + case RegOps.INVOKE_VIRTUAL: + return Dops.INVOKE_VIRTUAL; + case RegOps.INVOKE_SUPER: + return Dops.INVOKE_SUPER; + case RegOps.INVOKE_DIRECT: + return Dops.INVOKE_DIRECT; + case RegOps.INVOKE_INTERFACE: + return Dops.INVOKE_INTERFACE; + case RegOps.NEW_ARRAY: + return Dops.NEW_ARRAY; + case RegOps.FILLED_NEW_ARRAY: + return Dops.FILLED_NEW_ARRAY; + case RegOps.FILL_ARRAY_DATA: + return Dops.FILL_ARRAY_DATA; + case RegOps.MOVE_RESULT: { + RegisterSpec resultReg = insn.getResult(); + + if (resultReg == null) { + return Dops.NOP; + } else { + switch (resultReg.getBasicType()) { + case Type.BT_INT: + case Type.BT_FLOAT: + case Type.BT_BOOLEAN: + case Type.BT_BYTE: + case Type.BT_CHAR: + case Type.BT_SHORT: + return Dops.MOVE_RESULT; + case Type.BT_LONG: + case Type.BT_DOUBLE: + return Dops.MOVE_RESULT_WIDE; + case Type.BT_OBJECT: + return Dops.MOVE_RESULT_OBJECT; + default: { + throw new RuntimeException("Unexpected basic type"); } + } } - - throw new RuntimeException("unknown rop: " + rop); + } + + case RegOps.GET_FIELD: { + CstFieldRef ref = (CstFieldRef) ((ThrowingCstInsn) insn).getConstant(); + int basicType = ref.getBasicType(); + switch (basicType) { + case Type.BT_BOOLEAN: + return Dops.IGET_BOOLEAN; + case Type.BT_BYTE: + return Dops.IGET_BYTE; + case Type.BT_CHAR: + return Dops.IGET_CHAR; + case Type.BT_SHORT: + return Dops.IGET_SHORT; + case Type.BT_INT: + return Dops.IGET; + } + break; + } + case RegOps.PUT_FIELD: { + CstFieldRef ref = (CstFieldRef) ((ThrowingCstInsn) insn).getConstant(); + int basicType = ref.getBasicType(); + switch (basicType) { + case Type.BT_BOOLEAN: + return Dops.IPUT_BOOLEAN; + case Type.BT_BYTE: + return Dops.IPUT_BYTE; + case Type.BT_CHAR: + return Dops.IPUT_CHAR; + case Type.BT_SHORT: + return Dops.IPUT_SHORT; + case Type.BT_INT: + return Dops.IPUT; + } + break; + } + case RegOps.GET_STATIC: { + CstFieldRef ref = (CstFieldRef) ((ThrowingCstInsn) insn).getConstant(); + int basicType = ref.getBasicType(); + switch (basicType) { + case Type.BT_BOOLEAN: + return Dops.SGET_BOOLEAN; + case Type.BT_BYTE: + return Dops.SGET_BYTE; + case Type.BT_CHAR: + return Dops.SGET_CHAR; + case Type.BT_SHORT: + return Dops.SGET_SHORT; + case Type.BT_INT: + return Dops.SGET; + } + break; + } + case RegOps.PUT_STATIC: { + CstFieldRef ref = (CstFieldRef) ((ThrowingCstInsn) insn).getConstant(); + int basicType = ref.getBasicType(); + switch (basicType) { + case Type.BT_BOOLEAN: + return Dops.SPUT_BOOLEAN; + case Type.BT_BYTE: + return Dops.SPUT_BYTE; + case Type.BT_CHAR: + return Dops.SPUT_CHAR; + case Type.BT_SHORT: + return Dops.SPUT_SHORT; + case Type.BT_INT: + return Dops.SPUT; + } + break; + } + case RegOps.CONST: { + Constant cst = ((ThrowingCstInsn) insn).getConstant(); + if (cst instanceof CstType) { + return Dops.CONST_CLASS; + } else if (cst instanceof CstString) { + return Dops.CONST_STRING; + } + break; + } } + + throw new RuntimeException("unknown rop: " + rop); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/RopTranslator.java b/dx/src/com/android/jack/dx/dex/code/RopTranslator.java index 29b7400..ef0b7f6 100644 --- a/dx/src/com/android/jack/dx/dex/code/RopTranslator.java +++ b/dx/src/com/android/jack/dx/dex/code/RopTranslator.java @@ -47,836 +47,802 @@ import java.util.ArrayList; * #translate} method is the thing to call on this class. */ public final class RopTranslator { - /** {@code non-null;} options for dex output */ - private final DexOptions dexOptions; - /** {@code non-null;} method to translate */ - private final RopMethod method; - - /** - * how much position info to preserve; one of the static - * constants in {@link PositionList} + /** {@code non-null;} method to translate */ + private final RopMethod method; + + /** + * how much position info to preserve; one of the static + * constants in {@link PositionList} + */ + private final int positionInfo; + + /** {@code null-ok;} local variable info to use */ + private final LocalVariableInfo locals; + + /** {@code non-null;} container for all the address objects for the method */ + private final BlockAddresses addresses; + + /** {@code non-null;} list of output instructions in-progress */ + private final OutputCollector output; + + /** {@code non-null;} visitor to use during translation */ + private final TranslationVisitor translationVisitor; + + /** {@code >= 0;} register count for the method */ + private final int regCount; + + /** {@code null-ok;} block output order; becomes non-null in {@link #pickOrder} */ + private int[] order; + + /** size, in register units, of all the parameters to this method */ + private final int paramSize; + + /** + * true if the parameters to this method happen to be in proper order + * at the end of the frame (as the optimizer emits them) + */ + private boolean paramsAreInOrder; + + /** + * Translates a {@link RopMethod}. This may modify the given + * input. + * + * @param method {@code non-null;} the original method + * @param positionInfo how much position info to preserve; one of the + * static constants in {@link PositionList} + * @param locals {@code null-ok;} local variable information to use + * @param paramSize size, in register units, of all the parameters to + * this method + * @param dexOptions {@code non-null;} options for dex output + * @return {@code non-null;} the translated version + */ + public static DalvCode translate(RopMethod method, int positionInfo, LocalVariableInfo locals, + int paramSize, DexOptions dexOptions) { + RopTranslator translator = + new RopTranslator(method, positionInfo, locals, paramSize, dexOptions); + return translator.translateAndGetResult(); + } + + /** + * Constructs an instance. This method is private. Use {@link #translate}. + * + * @param method {@code non-null;} the original method + * @param positionInfo how much position info to preserve; one of the + * static constants in {@link PositionList} + * @param locals {@code null-ok;} local variable information to use + * @param paramSize size, in register units, of all the parameters to + * this method + * @param dexOptions {@code non-null;} options for dex output + */ + private RopTranslator(RopMethod method, int positionInfo, LocalVariableInfo locals, int paramSize, + DexOptions dexOptions) { + this.method = method; + this.positionInfo = positionInfo; + this.locals = locals; + this.addresses = new BlockAddresses(method); + this.paramSize = paramSize; + this.order = null; + this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize); + + BasicBlockList blocks = method.getBlocks(); + int bsz = blocks.size(); + + /* + * Max possible instructions includes three code address + * objects per basic block (to the first and last instruction, + * and just past the end of the block), and the possibility of + * an extra goto at the end of each basic block. */ - private final int positionInfo; - - /** {@code null-ok;} local variable info to use */ - private final LocalVariableInfo locals; - - /** {@code non-null;} container for all the address objects for the method */ - private final BlockAddresses addresses; + int maxInsns = (bsz * 3) + blocks.getInstructionCount(); + + if (locals != null) { + /* + * If we're tracking locals, then there's could be another + * extra instruction per block (for the locals state at the + * start of the block) as well as one for each interblock + * local introduction. + */ + maxInsns += bsz + locals.getAssignmentCount(); + } - /** {@code non-null;} list of output instructions in-progress */ - private final OutputCollector output; + /* + * If params are not in order, we will need register space + * for them before this is all over... + */ + this.regCount = blocks.getRegCount() + (paramsAreInOrder ? 0 : this.paramSize); - /** {@code non-null;} visitor to use during translation */ - private final TranslationVisitor translationVisitor; + this.output = new OutputCollector(dexOptions, maxInsns, bsz * 3, regCount); - /** {@code >= 0;} register count for the method */ - private final int regCount; + if (locals != null) { + this.translationVisitor = new LocalVariableAwareTranslationVisitor(output, locals); + } else { + this.translationVisitor = new TranslationVisitor(output); + } + } + + /** + * Checks to see if the move-param instructions that occur in this + * method happen to slot the params in an order at the top of the + * stack frame that matches dalvik's calling conventions. This will + * alway result in "true" for methods that have run through the + * SSA optimizer. + * + * @param paramSize size, in register units, of all the parameters + * to this method + */ + private static boolean calculateParamsAreInOrder(RopMethod method, final int paramSize) { + final boolean[] paramsAreInOrder = {true}; + final int initialRegCount = method.getBlocks().getRegCount(); + + /* + * We almost could just check the first block here, but the + * {@code cf} layer will put in a second move-param in a + * subsequent block in the case of synchronized methods. + */ + method.getBlocks().forEachInsn(new Insn.BaseVisitor() { + @Override + public void visitPlainCstInsn(PlainCstInsn insn) { + if (insn.getOpcode().getOpcode() == RegOps.MOVE_PARAM) { + int param = ((CstInteger) insn.getConstant()).getValue(); + + paramsAreInOrder[0] = paramsAreInOrder[0] + && ((initialRegCount - paramSize + param) == insn.getResult().getReg()); + } + } + }); + + return paramsAreInOrder[0]; + } + + /** + * Does the translation and returns the result. + * + * @return {@code non-null;} the result + */ + private DalvCode translateAndGetResult() { + pickOrder(); + outputInstructions(); + + StdCatchBuilder catches = new StdCatchBuilder(method, order, addresses); + + return new DalvCode(positionInfo, output.getFinisher(), catches); + } + + /** + * Performs initial creation of output instructions based on the + * original blocks. + */ + private void outputInstructions() { + BasicBlockList blocks = method.getBlocks(); + int[] order = this.order; + int len = order.length; + + // Process the blocks in output order. + for (int i = 0; i < len; i++) { + int nextI = i + 1; + int nextLabel = (nextI == order.length) ? -1 : order[nextI]; + outputBlock(blocks.labelToBlock(order[i]), nextLabel); + } + } + + /** + * Helper for {@link #outputInstructions}, which does the processing + * and output of one block. + * + * @param block {@code non-null;} the block to process and output + * @param nextLabel {@code >= -1;} the next block that will be processed, or + * {@code -1} if there is no next block + */ + private void outputBlock(BasicBlock block, int nextLabel) { + // Append the code address for this block. + CodeAddress startAddress = addresses.getStart(block); + output.add(startAddress); + + // Append the local variable state for the block. + if (locals != null) { + RegisterSpecSet starts = locals.getStarts(block); + output.add(new LocalSnapshot(startAddress.getPosition(), starts)); + } - /** {@code null-ok;} block output order; becomes non-null in {@link #pickOrder} */ - private int[] order; + /* + * Choose and append an output instruction for each original + * instruction. + */ + translationVisitor.setBlock(block, addresses.getLast(block)); + block.getInsns().forEach(translationVisitor); - /** size, in register units, of all the parameters to this method */ - private final int paramSize; + // Insert the block end code address. + output.add(addresses.getEnd(block)); - /** - * true if the parameters to this method happen to be in proper order - * at the end of the frame (as the optimizer emits them) - */ - private boolean paramsAreInOrder; + // Set up for end-of-block activities. - /** - * Translates a {@link RopMethod}. This may modify the given - * input. - * - * @param method {@code non-null;} the original method - * @param positionInfo how much position info to preserve; one of the - * static constants in {@link PositionList} - * @param locals {@code null-ok;} local variable information to use - * @param paramSize size, in register units, of all the parameters to - * this method - * @param dexOptions {@code non-null;} options for dex output - * @return {@code non-null;} the translated version - */ - public static DalvCode translate(RopMethod method, int positionInfo, - LocalVariableInfo locals, int paramSize, DexOptions dexOptions) { - RopTranslator translator = - new RopTranslator(method, positionInfo, locals, paramSize, dexOptions); - return translator.translateAndGetResult(); - } + int succ = block.getPrimarySuccessor(); + Insn lastInsn = block.getLastInsn(); - /** - * Constructs an instance. This method is private. Use {@link #translate}. - * - * @param method {@code non-null;} the original method - * @param positionInfo how much position info to preserve; one of the - * static constants in {@link PositionList} - * @param locals {@code null-ok;} local variable information to use - * @param paramSize size, in register units, of all the parameters to - * this method - * @param dexOptions {@code non-null;} options for dex output + /* + * Check for (and possibly correct for) a non-optimal choice of + * which block will get output next. */ - private RopTranslator(RopMethod method, int positionInfo, LocalVariableInfo locals, - int paramSize, DexOptions dexOptions) { - this.dexOptions = dexOptions; - this.method = method; - this.positionInfo = positionInfo; - this.locals = locals; - this.addresses = new BlockAddresses(method); - this.paramSize = paramSize; - this.order = null; - this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize); - - BasicBlockList blocks = method.getBlocks(); - int bsz = blocks.size(); +if ((succ >= 0) && (succ != nextLabel)) { + /* + * The block has a "primary successor" and that primary + * successor isn't the next block to be output. + */ + Rop lastRop = lastInsn.getOpcode(); + if ((lastRop.getBranchingness() == Rop.BRANCH_IF) + && (block.getSecondarySuccessor() == nextLabel)) { /* - * Max possible instructions includes three code address - * objects per basic block (to the first and last instruction, - * and just past the end of the block), and the possibility of - * an extra goto at the end of each basic block. + * The block ends with an "if" of some sort, and its + * secondary successor (the "then") is in fact the + * next block to output. So, reverse the sense of + * the test, so that we can just emit the next block + * without an interstitial goto. */ - int maxInsns = (bsz * 3) + blocks.getInstructionCount(); + output.reverseBranch(1, addresses.getStart(succ)); + } else { + /* + * Our only recourse is to add a goto here to get the + * flow to be correct. + */ + TargetInsn insn = new TargetInsn(Dops.GOTO, lastInsn.getPosition(), RegisterSpecList.EMPTY, + addresses.getStart(succ)); + output.add(insn); + } + } + } + + /** + * Picks an order for the blocks by doing "trace" analysis. + */ + private void pickOrder() { + BasicBlockList blocks = method.getBlocks(); + int sz = blocks.size(); + int maxLabel = blocks.getMaxLabel(); + int[] workSet = Bits.makeBitSet(maxLabel); + int[] tracebackSet = Bits.makeBitSet(maxLabel); + + for (int i = 0; i < sz; i++) { + BasicBlock one = blocks.get(i); + Bits.set(workSet, one.getLabel()); + } - if (locals != null) { + int[] order = new int[sz]; + int at = 0; + + /* + * Starting with the designated "first label" (that is, the + * first block of the method), add that label to the order, + * and then pick its first as-yet unordered successor to + * immediately follow it, giving top priority to the primary + * (aka default) successor (if any). Keep following successors + * until the trace runs out of possibilities. Then, continue + * by finding an unordered chain containing the first as-yet + * unordered block, and adding it to the order, and so on. + */ + for (int label = method.getFirstLabel(); label != -1; label = Bits.findFirst(workSet, 0)) { + + /* + * Attempt to trace backward from the chosen block to an + * as-yet unordered predecessor which lists the chosen + * block as its primary successor, and so on, until we + * fail to find such an unordered predecessor. Start the + * trace with that block. Note that the first block in the + * method has no predecessors, so in that case this loop + * will simply terminate with zero iterations and without + * picking a new starter block. + */ + traceBack: for (;;) { + IntList preds = method.labelToPredecessors(label); + int psz = preds.size(); + + for (int i = 0; i < psz; i++) { + int predLabel = preds.get(i); + + if (Bits.get(tracebackSet, predLabel)) { /* - * If we're tracking locals, then there's could be another - * extra instruction per block (for the locals state at the - * start of the block) as well as one for each interblock - * local introduction. + * We found a predecessor loop; stop tracing back + * from here. */ - maxInsns += bsz + locals.getAssignmentCount(); + break; + } + + if (!Bits.get(workSet, predLabel)) { + // This one's already ordered. + continue; + } + + BasicBlock pred = blocks.labelToBlock(predLabel); + if (pred.getPrimarySuccessor() == label) { + // Found one! + label = predLabel; + Bits.set(tracebackSet, label); + continue traceBack; + } } - /* - * If params are not in order, we will need register space - * for them before this is all over... - */ - this.regCount = blocks.getRegCount() - + (paramsAreInOrder ? 0 : this.paramSize); - - this.output = new OutputCollector(dexOptions, maxInsns, bsz * 3, regCount); + // Failed to find a better block to start the trace. + break; + } + + /* + * Trace a path from the chosen block to one of its + * unordered successors (hopefully the primary), and so + * on, until we run out of unordered successors. + */ + while (label != -1) { + Bits.clear(workSet, label); + Bits.clear(tracebackSet, label); + order[at] = label; + at++; + + BasicBlock one = blocks.labelToBlock(label); + BasicBlock preferredBlock = blocks.preferredSuccessorOf(one); + + if (preferredBlock == null) { + break; + } - if (locals != null) { - this.translationVisitor = - new LocalVariableAwareTranslationVisitor(output, locals); + int preferred = preferredBlock.getLabel(); + int primary = one.getPrimarySuccessor(); + + if (Bits.get(workSet, preferred)) { + /* + * Order the current block's preferred successor + * next, as it has yet to be scheduled. + */ + label = preferred; + } else if ((primary != preferred) && (primary >= 0) && Bits.get(workSet, primary)) { + /* + * The primary is available, so use that. + */ + label = primary; } else { - this.translationVisitor = new TranslationVisitor(output); + /* + * There's no obvious candidate, so pick the first + * one that's available, if any. + */ + IntList successors = one.getSuccessors(); + int ssz = successors.size(); + label = -1; + for (int i = 0; i < ssz; i++) { + int candidate = successors.get(i); + if (Bits.get(workSet, candidate)) { + label = candidate; + break; + } + } } + } } - /** - * Checks to see if the move-param instructions that occur in this - * method happen to slot the params in an order at the top of the - * stack frame that matches dalvik's calling conventions. This will - * alway result in "true" for methods that have run through the - * SSA optimizer. - * - * @param paramSize size, in register units, of all the parameters - * to this method - */ - private static boolean calculateParamsAreInOrder(RopMethod method, - final int paramSize) { - final boolean[] paramsAreInOrder = { true }; - final int initialRegCount = method.getBlocks().getRegCount(); + if (at != sz) { + // There was a duplicate block label. + throw new RuntimeException("shouldn't happen"); + } - /* - * We almost could just check the first block here, but the - * {@code cf} layer will put in a second move-param in a - * subsequent block in the case of synchronized methods. - */ - method.getBlocks().forEachInsn(new Insn.BaseVisitor() { - @Override - public void visitPlainCstInsn(PlainCstInsn insn) { - if (insn.getOpcode().getOpcode()== RegOps.MOVE_PARAM) { - int param = - ((CstInteger) insn.getConstant()).getValue(); - - paramsAreInOrder[0] = paramsAreInOrder[0] - && ((initialRegCount - paramSize + param) - == insn.getResult().getReg()); - } - } - }); + this.order = order; + } + + /** + * Gets the complete register list (result and sources) out of a + * given rop instruction. For insns that are commutative, have + * two register sources, and have a source equal to the result, + * place that source first. + * + * @param insn {@code non-null;} instruction in question + * @return {@code non-null;} the instruction's complete register list + */ + private static RegisterSpecList getRegs(Insn insn) { + return getRegs(insn, insn.getResult()); + } + + /** + * Gets the complete register list (result and sources) out of a + * given rop instruction. For insns that are commutative, have + * two register sources, and have a source equal to the result, + * place that source first. + * + * @param insn {@code non-null;} instruction in question + * @param resultReg {@code null-ok;} the real result to use (ignore the insn's) + * @return {@code non-null;} the instruction's complete register list + */ + private static RegisterSpecList getRegs(Insn insn, RegisterSpec resultReg) { + RegisterSpecList regs = insn.getSources(); + + if (insn.getOpcode().isCommutative() && (regs.size() == 2) + && (resultReg.getReg() == regs.get(1).getReg())) { + + /* + * For commutative ops which have two register sources, + * if the second source is the same register as the result, + * swap the sources so that an opcode of form 12x can be selected + * instead of one of form 23x + */ + +regs = RegisterSpecList.make(regs.get(1), regs.get(0)); + } - return paramsAreInOrder[0]; + if (resultReg == null) { + return regs; } - /** - * Does the translation and returns the result. - * - * @return {@code non-null;} the result - */ - private DalvCode translateAndGetResult() { - pickOrder(); - outputInstructions(); + return regs.withFirst(resultReg); + } - StdCatchBuilder catches = - new StdCatchBuilder(method, order, addresses); + /** + * Instruction visitor class for doing the instruction translation per se. + */ + private class TranslationVisitor implements Insn.Visitor { + /** {@code non-null;} list of output instructions in-progress */ + private final OutputCollector output; - return new DalvCode(positionInfo, output.getFinisher(), catches); - } + /** {@code non-null;} basic block being worked on */ + private BasicBlock block; /** - * Performs initial creation of output instructions based on the - * original blocks. + * {@code null-ok;} code address for the salient last instruction of the + * block (used before switches and throwing instructions) */ - private void outputInstructions() { - BasicBlockList blocks = method.getBlocks(); - int[] order = this.order; - int len = order.length; - - // Process the blocks in output order. - for (int i = 0; i < len; i++) { - int nextI = i + 1; - int nextLabel = (nextI == order.length) ? -1 : order[nextI]; - outputBlock(blocks.labelToBlock(order[i]), nextLabel); - } - } + private CodeAddress lastAddress; /** - * Helper for {@link #outputInstructions}, which does the processing - * and output of one block. + * Constructs an instance. * - * @param block {@code non-null;} the block to process and output - * @param nextLabel {@code >= -1;} the next block that will be processed, or - * {@code -1} if there is no next block + * @param output {@code non-null;} destination for instruction output */ - private void outputBlock(BasicBlock block, int nextLabel) { - // Append the code address for this block. - CodeAddress startAddress = addresses.getStart(block); - output.add(startAddress); - - // Append the local variable state for the block. - if (locals != null) { - RegisterSpecSet starts = locals.getStarts(block); - output.add(new LocalSnapshot(startAddress.getPosition(), - starts)); - } - - /* - * Choose and append an output instruction for each original - * instruction. - */ - translationVisitor.setBlock(block, addresses.getLast(block)); - block.getInsns().forEach(translationVisitor); - - // Insert the block end code address. - output.add(addresses.getEnd(block)); - - // Set up for end-of-block activities. - - int succ = block.getPrimarySuccessor(); - Insn lastInsn = block.getLastInsn(); - - /* - * Check for (and possibly correct for) a non-optimal choice of - * which block will get output next. - */ - - if ((succ >= 0) && (succ != nextLabel)) { - /* - * The block has a "primary successor" and that primary - * successor isn't the next block to be output. - */ - Rop lastRop = lastInsn.getOpcode(); - if ((lastRop.getBranchingness() == Rop.BRANCH_IF) && - (block.getSecondarySuccessor() == nextLabel)) { - /* - * The block ends with an "if" of some sort, and its - * secondary successor (the "then") is in fact the - * next block to output. So, reverse the sense of - * the test, so that we can just emit the next block - * without an interstitial goto. - */ - output.reverseBranch(1, addresses.getStart(succ)); - } else { - /* - * Our only recourse is to add a goto here to get the - * flow to be correct. - */ - TargetInsn insn = - new TargetInsn(Dops.GOTO, lastInsn.getPosition(), - RegisterSpecList.EMPTY, - addresses.getStart(succ)); - output.add(insn); - } - } + public TranslationVisitor(OutputCollector output) { + this.output = output; } /** - * Picks an order for the blocks by doing "trace" analysis. + * Sets the block currently being worked on. + * + * @param block {@code non-null;} the block + * @param lastAddress {@code non-null;} code address for the salient + * last instruction of the block */ - private void pickOrder() { - BasicBlockList blocks = method.getBlocks(); - int sz = blocks.size(); - int maxLabel = blocks.getMaxLabel(); - int[] workSet = Bits.makeBitSet(maxLabel); - int[] tracebackSet = Bits.makeBitSet(maxLabel); - - for (int i = 0; i < sz; i++) { - BasicBlock one = blocks.get(i); - Bits.set(workSet, one.getLabel()); - } - - int[] order = new int[sz]; - int at = 0; + public void setBlock(BasicBlock block, CodeAddress lastAddress) { + this.block = block; + this.lastAddress = lastAddress; + } + /** {@inheritDoc} */ + @Override + public void visitPlainInsn(PlainInsn insn) { + Rop rop = insn.getOpcode(); + if (rop.getOpcode() == RegOps.MARK_LOCAL) { /* - * Starting with the designated "first label" (that is, the - * first block of the method), add that label to the order, - * and then pick its first as-yet unordered successor to - * immediately follow it, giving top priority to the primary - * (aka default) successor (if any). Keep following successors - * until the trace runs out of possibilities. Then, continue - * by finding an unordered chain containing the first as-yet - * unordered block, and adding it to the order, and so on. + * Ignore these. They're dealt with by + * the LocalVariableAwareTranslationVisitor */ - for (int label = method.getFirstLabel(); - label != -1; - label = Bits.findFirst(workSet, 0)) { - - /* - * Attempt to trace backward from the chosen block to an - * as-yet unordered predecessor which lists the chosen - * block as its primary successor, and so on, until we - * fail to find such an unordered predecessor. Start the - * trace with that block. Note that the first block in the - * method has no predecessors, so in that case this loop - * will simply terminate with zero iterations and without - * picking a new starter block. - */ - traceBack: - for (;;) { - IntList preds = method.labelToPredecessors(label); - int psz = preds.size(); - - for (int i = 0; i < psz; i++) { - int predLabel = preds.get(i); - - if (Bits.get(tracebackSet, predLabel)) { - /* - * We found a predecessor loop; stop tracing back - * from here. - */ - break; - } - - if (!Bits.get(workSet, predLabel)) { - // This one's already ordered. - continue; - } - - BasicBlock pred = blocks.labelToBlock(predLabel); - if (pred.getPrimarySuccessor() == label) { - // Found one! - label = predLabel; - Bits.set(tracebackSet, label); - continue traceBack; - } - } - - // Failed to find a better block to start the trace. - break; - } - - /* - * Trace a path from the chosen block to one of its - * unordered successors (hopefully the primary), and so - * on, until we run out of unordered successors. - */ - while (label != -1) { - Bits.clear(workSet, label); - Bits.clear(tracebackSet, label); - order[at] = label; - at++; - - BasicBlock one = blocks.labelToBlock(label); - BasicBlock preferredBlock = blocks.preferredSuccessorOf(one); - - if (preferredBlock == null) { - break; - } - - int preferred = preferredBlock.getLabel(); - int primary = one.getPrimarySuccessor(); - - if (Bits.get(workSet, preferred)) { - /* - * Order the current block's preferred successor - * next, as it has yet to be scheduled. - */ - label = preferred; - } else if ((primary != preferred) && (primary >= 0) - && Bits.get(workSet, primary)) { - /* - * The primary is available, so use that. - */ - label = primary; - } else { - /* - * There's no obvious candidate, so pick the first - * one that's available, if any. - */ - IntList successors = one.getSuccessors(); - int ssz = successors.size(); - label = -1; - for (int i = 0; i < ssz; i++) { - int candidate = successors.get(i); - if (Bits.get(workSet, candidate)) { - label = candidate; - break; - } - } - } - } + return; + } + if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { + // These get skipped + return; + } + + SourcePosition pos = insn.getPosition(); + Dop opcode = RopToDop.dopFor(insn); + DalvInsn di; + + switch (rop.getBranchingness()) { + case Rop.BRANCH_NONE: + case Rop.BRANCH_RETURN: + case Rop.BRANCH_THROW: { + di = new SimpleInsn(opcode, pos, getRegs(insn)); + break; } - - if (at != sz) { - // There was a duplicate block label. - throw new RuntimeException("shouldn't happen"); + case Rop.BRANCH_GOTO: { + /* + * Code in the main translation loop will emit a + * goto if necessary (if the branch isn't to the + * immediately subsequent block). + */ + return; + } + case Rop.BRANCH_IF: { + int target = block.getSuccessors().get(1); + di = new TargetInsn(opcode, pos, getRegs(insn), addresses.getStart(target)); + break; } + default: { + throw new RuntimeException("shouldn't happen"); + } + } - this.order = order; + addOutput(di); } - /** - * Gets the complete register list (result and sources) out of a - * given rop instruction. For insns that are commutative, have - * two register sources, and have a source equal to the result, - * place that source first. - * - * @param insn {@code non-null;} instruction in question - * @return {@code non-null;} the instruction's complete register list - */ - private static RegisterSpecList getRegs(Insn insn) { - return getRegs(insn, insn.getResult()); + /** {@inheritDoc} */ + @Override + public void visitPlainCstInsn(PlainCstInsn insn) { + SourcePosition pos = insn.getPosition(); + Dop opcode = RopToDop.dopFor(insn); + Rop rop = insn.getOpcode(); + int ropOpcode = rop.getOpcode(); + DalvInsn di; + + if (rop.getBranchingness() != Rop.BRANCH_NONE) { + throw new RuntimeException("shouldn't happen"); + } + + if (ropOpcode == RegOps.MOVE_PARAM) { + if (!paramsAreInOrder) { + /* + * Parameters are not in order at the top of the reg space. + * We need to add moves. + */ + +RegisterSpec dest = insn.getResult(); + int param = ((CstInteger) insn.getConstant()).getValue(); + RegisterSpec source = RegisterSpec.make(regCount - paramSize + param, dest.getType()); + di = new SimpleInsn(opcode, pos, RegisterSpecList.make(dest, source)); + addOutput(di); + } + } else { + // No moves required for the parameters + RegisterSpecList regs = getRegs(insn); + di = new CstInsn(opcode, pos, regs, insn.getConstant()); + addOutput(di); + } + } + + /** {@inheritDoc} */ + @Override + public void visitSwitchInsn(SwitchInsn insn) { + SourcePosition pos = insn.getPosition(); + IntList cases = insn.getCases(); + IntList successors = block.getSuccessors(); + int casesSz = cases.size(); + int succSz = successors.size(); + int primarySuccessor = block.getPrimarySuccessor(); + + /* + * Check the assumptions that the number of cases is one + * less than the number of successors and that the last + * successor in the list is the primary (in this case, the + * default). This test is here to guard against forgetting + * to change this code if the way switch instructions are + * constructed also gets changed. + */ + if ((casesSz != (succSz - 1)) || (primarySuccessor != successors.get(casesSz))) { + throw new RuntimeException("shouldn't happen"); + } + + CodeAddress[] switchTargets = new CodeAddress[casesSz]; + + for (int i = 0; i < casesSz; i++) { + int label = successors.get(i); + switchTargets[i] = addresses.getStart(label); + } + + CodeAddress dataAddress = new CodeAddress(pos); + // make a new address that binds closely to the switch instruction + CodeAddress switchAddress = new CodeAddress(lastAddress.getPosition(), true); + SwitchData dataInsn = new SwitchData(pos, switchAddress, cases, switchTargets); + Dop opcode = dataInsn.isPacked() ? Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH; + TargetInsn switchInsn = new TargetInsn(opcode, pos, getRegs(insn), dataAddress); + + addOutput(switchAddress); + addOutput(switchInsn); + + addOutputSuffix(new OddSpacer(pos)); + addOutputSuffix(dataAddress); + addOutputSuffix(dataInsn); } /** - * Gets the complete register list (result and sources) out of a - * given rop instruction. For insns that are commutative, have - * two register sources, and have a source equal to the result, - * place that source first. + * Looks forward to the current block's primary successor, returning + * the RegisterSpec of the result of the move-result-pseudo at the + * top of that block or null if none. * - * @param insn {@code non-null;} instruction in question - * @param resultReg {@code null-ok;} the real result to use (ignore the insn's) - * @return {@code non-null;} the instruction's complete register list + * @return {@code null-ok;} result of move-result-pseudo at the beginning of + * primary successor */ - private static RegisterSpecList getRegs(Insn insn, - RegisterSpec resultReg) { - RegisterSpecList regs = insn.getSources(); + private RegisterSpec getNextMoveResultPseudo() { + int label = block.getPrimarySuccessor(); - if (insn.getOpcode().isCommutative() - && (regs.size() == 2) - && (resultReg.getReg() == regs.get(1).getReg())) { + if (label < 0) { + return null; + } - /* - * For commutative ops which have two register sources, - * if the second source is the same register as the result, - * swap the sources so that an opcode of form 12x can be selected - * instead of one of form 23x - */ - - regs = RegisterSpecList.make(regs.get(1), regs.get(0)); - } + Insn insn = method.getBlocks().labelToBlock(label).getInsns().get(0); - if (resultReg == null) { - return regs; - } - - return regs.withFirst(resultReg); + if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) { + return null; + } else { + return insn.getResult(); + } } - /** - * Instruction visitor class for doing the instruction translation per se. - */ - private class TranslationVisitor implements Insn.Visitor { - /** {@code non-null;} list of output instructions in-progress */ - private final OutputCollector output; - - /** {@code non-null;} basic block being worked on */ - private BasicBlock block; - - /** - * {@code null-ok;} code address for the salient last instruction of the - * block (used before switches and throwing instructions) - */ - private CodeAddress lastAddress; - - /** - * Constructs an instance. - * - * @param output {@code non-null;} destination for instruction output - */ - public TranslationVisitor(OutputCollector output) { - this.output = output; - } + /** {@inheritDoc} */ + @Override + public void visitThrowingCstInsn(ThrowingCstInsn insn) { + SourcePosition pos = insn.getPosition(); + Dop opcode = RopToDop.dopFor(insn); + Rop rop = insn.getOpcode(); + Constant cst = insn.getConstant(); - /** - * Sets the block currently being worked on. - * - * @param block {@code non-null;} the block - * @param lastAddress {@code non-null;} code address for the salient - * last instruction of the block - */ - public void setBlock(BasicBlock block, CodeAddress lastAddress) { - this.block = block; - this.lastAddress = lastAddress; - } + if (rop.getBranchingness() != Rop.BRANCH_THROW) { + throw new RuntimeException("shouldn't happen"); + } - /** {@inheritDoc} */ - public void visitPlainInsn(PlainInsn insn) { - Rop rop = insn.getOpcode(); - if (rop.getOpcode() == RegOps.MARK_LOCAL) { - /* - * Ignore these. They're dealt with by - * the LocalVariableAwareTranslationVisitor - */ - return; - } - if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { - // These get skipped - return; - } + addOutput(lastAddress); - SourcePosition pos = insn.getPosition(); - Dop opcode = RopToDop.dopFor(insn); - DalvInsn di; - - switch (rop.getBranchingness()) { - case Rop.BRANCH_NONE: - case Rop.BRANCH_RETURN: - case Rop.BRANCH_THROW: { - di = new SimpleInsn(opcode, pos, getRegs(insn)); - break; - } - case Rop.BRANCH_GOTO: { - /* - * Code in the main translation loop will emit a - * goto if necessary (if the branch isn't to the - * immediately subsequent block). - */ - return; - } - case Rop.BRANCH_IF: { - int target = block.getSuccessors().get(1); - di = new TargetInsn(opcode, pos, getRegs(insn), - addresses.getStart(target)); - break; - } - default: { - throw new RuntimeException("shouldn't happen"); - } - } + if (rop.isCallLike()) { + RegisterSpecList regs = insn.getSources(); + DalvInsn di = new CstInsn(opcode, pos, regs, cst); - addOutput(di); - } + addOutput(di); + } else { + RegisterSpec realResult = getNextMoveResultPseudo(); - /** {@inheritDoc} */ - public void visitPlainCstInsn(PlainCstInsn insn) { - SourcePosition pos = insn.getPosition(); - Dop opcode = RopToDop.dopFor(insn); - Rop rop = insn.getOpcode(); - int ropOpcode = rop.getOpcode(); - DalvInsn di; + RegisterSpecList regs = getRegs(insn, realResult); + DalvInsn di; - if (rop.getBranchingness() != Rop.BRANCH_NONE) { - throw new RuntimeException("shouldn't happen"); - } + boolean hasResult = opcode.hasResult() || (rop.getOpcode() == RegOps.CHECK_CAST); - if (ropOpcode == RegOps.MOVE_PARAM) { - if (!paramsAreInOrder) { - /* - * Parameters are not in order at the top of the reg space. - * We need to add moves. - */ - - RegisterSpec dest = insn.getResult(); - int param = - ((CstInteger) insn.getConstant()).getValue(); - RegisterSpec source = - RegisterSpec.make(regCount - paramSize + param, - dest.getType()); - di = new SimpleInsn(opcode, pos, - RegisterSpecList.make(dest, source)); - addOutput(di); - } - } else { - // No moves required for the parameters - RegisterSpecList regs = getRegs(insn); - di = new CstInsn(opcode, pos, regs, insn.getConstant()); - addOutput(di); - } + if (hasResult != (realResult != null)) { + throw new RuntimeException("Insn with result/move-result-pseudo mismatch " + insn); } - /** {@inheritDoc} */ - public void visitSwitchInsn(SwitchInsn insn) { - SourcePosition pos = insn.getPosition(); - IntList cases = insn.getCases(); - IntList successors = block.getSuccessors(); - int casesSz = cases.size(); - int succSz = successors.size(); - int primarySuccessor = block.getPrimarySuccessor(); - - /* - * Check the assumptions that the number of cases is one - * less than the number of successors and that the last - * successor in the list is the primary (in this case, the - * default). This test is here to guard against forgetting - * to change this code if the way switch instructions are - * constructed also gets changed. - */ - if ((casesSz != (succSz - 1)) || - (primarySuccessor != successors.get(casesSz))) { - throw new RuntimeException("shouldn't happen"); - } - - CodeAddress[] switchTargets = new CodeAddress[casesSz]; - - for (int i = 0; i < casesSz; i++) { - int label = successors.get(i); - switchTargets[i] = addresses.getStart(label); - } - - CodeAddress dataAddress = new CodeAddress(pos); - // make a new address that binds closely to the switch instruction - CodeAddress switchAddress = - new CodeAddress(lastAddress.getPosition(), true); - SwitchData dataInsn = - new SwitchData(pos, switchAddress, cases, switchTargets); - Dop opcode = dataInsn.isPacked() ? - Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH; - TargetInsn switchInsn = - new TargetInsn(opcode, pos, getRegs(insn), dataAddress); - - addOutput(switchAddress); - addOutput(switchInsn); - - addOutputSuffix(new OddSpacer(pos)); - addOutputSuffix(dataAddress); - addOutputSuffix(dataInsn); + if ((rop.getOpcode() == RegOps.NEW_ARRAY) && (opcode.getOpcode() != Opcodes.NEW_ARRAY)) { + /* + * It's a type-specific new-array-<primitive>, and + * so it should be turned into a SimpleInsn (no + * constant ref as it's implicit). + */ + di = new SimpleInsn(opcode, pos, regs); + } else { + /* + * This is the general case for constant-bearing + * instructions. + */ + di = new CstInsn(opcode, pos, regs, cst); } - /** - * Looks forward to the current block's primary successor, returning - * the RegisterSpec of the result of the move-result-pseudo at the - * top of that block or null if none. - * - * @return {@code null-ok;} result of move-result-pseudo at the beginning of - * primary successor - */ - private RegisterSpec getNextMoveResultPseudo() - { - int label = block.getPrimarySuccessor(); - - if (label < 0) { - return null; - } + addOutput(di); + } + } - Insn insn - = method.getBlocks().labelToBlock(label).getInsns().get(0); + /** {@inheritDoc} */ + @Override + public void visitThrowingInsn(ThrowingInsn insn) { + SourcePosition pos = insn.getPosition(); + Dop opcode = RopToDop.dopFor(insn); + Rop rop = insn.getOpcode(); + RegisterSpec realResult; - if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) { - return null; - } else { - return insn.getResult(); - } - } + if (rop.getBranchingness() != Rop.BRANCH_THROW) { + throw new RuntimeException("shouldn't happen"); + } - /** {@inheritDoc} */ - public void visitThrowingCstInsn(ThrowingCstInsn insn) { - SourcePosition pos = insn.getPosition(); - Dop opcode = RopToDop.dopFor(insn); - Rop rop = insn.getOpcode(); - Constant cst = insn.getConstant(); + realResult = getNextMoveResultPseudo(); - if (rop.getBranchingness() != Rop.BRANCH_THROW) { - throw new RuntimeException("shouldn't happen"); - } + if (opcode.hasResult() != (realResult != null)) { + throw new RuntimeException("Insn with result/move-result-pseudo mismatch" + insn); + } - addOutput(lastAddress); - - if (rop.isCallLike()) { - RegisterSpecList regs = insn.getSources(); - DalvInsn di = new CstInsn(opcode, pos, regs, cst); - - addOutput(di); - } else { - RegisterSpec realResult = getNextMoveResultPseudo(); - - RegisterSpecList regs = getRegs(insn, realResult); - DalvInsn di; - - boolean hasResult = opcode.hasResult() - || (rop.getOpcode() == RegOps.CHECK_CAST); - - if (hasResult != (realResult != null)) { - throw new RuntimeException( - "Insn with result/move-result-pseudo mismatch " + - insn); - } - - if ((rop.getOpcode() == RegOps.NEW_ARRAY) && - (opcode.getOpcode() != Opcodes.NEW_ARRAY)) { - /* - * It's a type-specific new-array-<primitive>, and - * so it should be turned into a SimpleInsn (no - * constant ref as it's implicit). - */ - di = new SimpleInsn(opcode, pos, regs); - } else { - /* - * This is the general case for constant-bearing - * instructions. - */ - di = new CstInsn(opcode, pos, regs, cst); - } - - addOutput(di); - } - } - - /** {@inheritDoc} */ - public void visitThrowingInsn(ThrowingInsn insn) { - SourcePosition pos = insn.getPosition(); - Dop opcode = RopToDop.dopFor(insn); - Rop rop = insn.getOpcode(); - RegisterSpec realResult; + addOutput(lastAddress); - if (rop.getBranchingness() != Rop.BRANCH_THROW) { - throw new RuntimeException("shouldn't happen"); - } + DalvInsn di = new SimpleInsn(opcode, pos, getRegs(insn, realResult)); - realResult = getNextMoveResultPseudo(); - - if (opcode.hasResult() != (realResult != null)) { - throw new RuntimeException( - "Insn with result/move-result-pseudo mismatch" + insn); - } + addOutput(di); + } - addOutput(lastAddress); + /** {@inheritDoc} */ + @Override + public void visitFillArrayDataInsn(FillArrayDataInsn insn) { + SourcePosition pos = insn.getPosition(); + Constant cst = insn.getConstant(); + ArrayList<Constant> values = insn.getInitValues(); + Rop rop = insn.getOpcode(); + + if (rop.getBranchingness() != Rop.BRANCH_NONE) { + throw new RuntimeException("shouldn't happen"); + } + CodeAddress dataAddress = new CodeAddress(pos); + ArrayData dataInsn = new ArrayData(pos, lastAddress, values, cst); + + TargetInsn fillArrayDataInsn = + new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn), dataAddress); + + addOutput(lastAddress); + addOutput(fillArrayDataInsn); + + addOutputSuffix(new OddSpacer(pos)); + addOutputSuffix(dataAddress); + addOutputSuffix(dataInsn); + } - DalvInsn di = new SimpleInsn(opcode, pos, - getRegs(insn, realResult)); + /** + * Adds to the output. + * + * @param insn {@code non-null;} instruction to add + */ + protected void addOutput(DalvInsn insn) { + output.add(insn); + } - addOutput(di); - } + /** + * Adds to the output suffix. + * + * @param insn {@code non-null;} instruction to add + */ + protected void addOutputSuffix(DalvInsn insn) { + output.addSuffix(insn); + } + } - /** {@inheritDoc} */ - public void visitFillArrayDataInsn(FillArrayDataInsn insn) { - SourcePosition pos = insn.getPosition(); - Constant cst = insn.getConstant(); - ArrayList<Constant> values = insn.getInitValues(); - Rop rop = insn.getOpcode(); + /** + * Instruction visitor class for doing instruction translation with + * local variable tracking + */ + private class LocalVariableAwareTranslationVisitor extends TranslationVisitor { + /** {@code non-null;} local variable info */ + private LocalVariableInfo locals; - if (rop.getBranchingness() != Rop.BRANCH_NONE) { - throw new RuntimeException("shouldn't happen"); - } - CodeAddress dataAddress = new CodeAddress(pos); - ArrayData dataInsn = - new ArrayData(pos, lastAddress, values, cst); + /** + * Constructs an instance. + * + * @param output {@code non-null;} destination for instruction output + * @param locals {@code non-null;} the local variable info + */ + public LocalVariableAwareTranslationVisitor(OutputCollector output, LocalVariableInfo locals) { + super(output); + this.locals = locals; + } - TargetInsn fillArrayDataInsn = - new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn), - dataAddress); + /** {@inheritDoc} */ + @Override + public void visitPlainInsn(PlainInsn insn) { + super.visitPlainInsn(insn); + addIntroductionIfNecessary(insn); + } - addOutput(lastAddress); - addOutput(fillArrayDataInsn); + /** {@inheritDoc} */ + @Override + public void visitPlainCstInsn(PlainCstInsn insn) { + super.visitPlainCstInsn(insn); + addIntroductionIfNecessary(insn); + } - addOutputSuffix(new OddSpacer(pos)); - addOutputSuffix(dataAddress); - addOutputSuffix(dataInsn); - } + /** {@inheritDoc} */ + @Override + public void visitSwitchInsn(SwitchInsn insn) { + super.visitSwitchInsn(insn); + addIntroductionIfNecessary(insn); + } - /** - * Adds to the output. - * - * @param insn {@code non-null;} instruction to add - */ - protected void addOutput(DalvInsn insn) { - output.add(insn); - } + /** {@inheritDoc} */ + @Override + public void visitThrowingCstInsn(ThrowingCstInsn insn) { + super.visitThrowingCstInsn(insn); + addIntroductionIfNecessary(insn); + } - /** - * Adds to the output suffix. - * - * @param insn {@code non-null;} instruction to add - */ - protected void addOutputSuffix(DalvInsn insn) { - output.addSuffix(insn); - } + /** {@inheritDoc} */ + @Override + public void visitThrowingInsn(ThrowingInsn insn) { + super.visitThrowingInsn(insn); + addIntroductionIfNecessary(insn); } /** - * Instruction visitor class for doing instruction translation with - * local variable tracking + * Adds a {@link LocalStart} to the output if the given + * instruction in fact introduces a local variable. + * + * @param insn {@code non-null;} instruction in question */ - private class LocalVariableAwareTranslationVisitor - extends TranslationVisitor { - /** {@code non-null;} local variable info */ - private LocalVariableInfo locals; - - /** - * Constructs an instance. - * - * @param output {@code non-null;} destination for instruction output - * @param locals {@code non-null;} the local variable info - */ - public LocalVariableAwareTranslationVisitor(OutputCollector output, - LocalVariableInfo locals) { - super(output); - this.locals = locals; - } - - /** {@inheritDoc} */ - @Override - public void visitPlainInsn(PlainInsn insn) { - super.visitPlainInsn(insn); - addIntroductionIfNecessary(insn); - } - - /** {@inheritDoc} */ - @Override - public void visitPlainCstInsn(PlainCstInsn insn) { - super.visitPlainCstInsn(insn); - addIntroductionIfNecessary(insn); - } + public void addIntroductionIfNecessary(Insn insn) { + RegisterSpec spec = locals.getAssignment(insn); - /** {@inheritDoc} */ - @Override - public void visitSwitchInsn(SwitchInsn insn) { - super.visitSwitchInsn(insn); - addIntroductionIfNecessary(insn); - } - - /** {@inheritDoc} */ - @Override - public void visitThrowingCstInsn(ThrowingCstInsn insn) { - super.visitThrowingCstInsn(insn); - addIntroductionIfNecessary(insn); - } - - /** {@inheritDoc} */ - @Override - public void visitThrowingInsn(ThrowingInsn insn) { - super.visitThrowingInsn(insn); - addIntroductionIfNecessary(insn); - } - - /** - * Adds a {@link LocalStart} to the output if the given - * instruction in fact introduces a local variable. - * - * @param insn {@code non-null;} instruction in question - */ - public void addIntroductionIfNecessary(Insn insn) { - RegisterSpec spec = locals.getAssignment(insn); - - if (spec != null) { - addOutput(new LocalStart(insn.getPosition(), spec)); - } - } + if (spec != null) { + addOutput(new LocalStart(insn.getPosition(), spec)); + } } + } } diff --git a/dx/src/com/android/jack/dx/dex/code/SimpleInsn.java b/dx/src/com/android/jack/dx/dex/code/SimpleInsn.java index ca233e4..351c218 100644 --- a/dx/src/com/android/jack/dx/dex/code/SimpleInsn.java +++ b/dx/src/com/android/jack/dx/dex/code/SimpleInsn.java @@ -24,36 +24,35 @@ import com.android.jack.dx.rop.code.SourcePosition; * the base class. */ public final class SimpleInsn extends FixedSizeInsn { - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param opcode the opcode; one of the constants from {@link Dops} - * @param position {@code non-null;} source position - * @param registers {@code non-null;} register list, including a - * result register if appropriate (that is, registers may be either - * ins or outs) - */ - public SimpleInsn(Dop opcode, SourcePosition position, - RegisterSpecList registers) { - super(opcode, position, registers); - } + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param opcode the opcode; one of the constants from {@link Dops} + * @param position {@code non-null;} source position + * @param registers {@code non-null;} register list, including a + * result register if appropriate (that is, registers may be either + * ins or outs) + */ + public SimpleInsn(Dop opcode, SourcePosition position, RegisterSpecList registers) { + super(opcode, position, registers); + } - /** {@inheritDoc} */ - @Override - public DalvInsn withOpcode(Dop opcode) { - return new SimpleInsn(opcode, getPosition(), getRegisters()); - } + /** {@inheritDoc} */ + @Override + public DalvInsn withOpcode(Dop opcode) { + return new SimpleInsn(opcode, getPosition(), getRegisters()); + } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new SimpleInsn(getOpcode(), getPosition(), registers); - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new SimpleInsn(getOpcode(), getPosition(), registers); + } - /** {@inheritDoc} */ - @Override - protected String argString() { - return null; - } + /** {@inheritDoc} */ + @Override + protected String argString() { + return null; + } } diff --git a/dx/src/com/android/jack/dx/dex/code/StdCatchBuilder.java b/dx/src/com/android/jack/dx/dex/code/StdCatchBuilder.java index e7f75eb..04c6e0b 100644 --- a/dx/src/com/android/jack/dx/dex/code/StdCatchBuilder.java +++ b/dx/src/com/android/jack/dx/dex/code/StdCatchBuilder.java @@ -32,285 +32,275 @@ import java.util.HashSet; * and associated data. */ public final class StdCatchBuilder implements CatchBuilder { - /** the maximum range of a single catch handler, in code units */ - private static final int MAX_CATCH_RANGE = 65535; - - /** {@code non-null;} method to build the list for */ - private final RopMethod method; + /** the maximum range of a single catch handler, in code units */ + private static final int MAX_CATCH_RANGE = 65535; + + /** {@code non-null;} method to build the list for */ + private final RopMethod method; + + /** {@code non-null;} block output order */ + private final int[] order; + + /** {@code non-null;} address objects for each block */ + private final BlockAddresses addresses; + + /** + * Constructs an instance. It merely holds onto its parameters for + * a subsequent call to {@link #build}. + * + * @param method {@code non-null;} method to build the list for + * @param order {@code non-null;} block output order + * @param addresses {@code non-null;} address objects for each block + */ + public StdCatchBuilder(RopMethod method, int[] order, BlockAddresses addresses) { + if (method == null) { + throw new NullPointerException("method == null"); + } - /** {@code non-null;} block output order */ - private final int[] order; + if (order == null) { + throw new NullPointerException("order == null"); + } - /** {@code non-null;} address objects for each block */ - private final BlockAddresses addresses; + if (addresses == null) { + throw new NullPointerException("addresses == null"); + } - /** - * Constructs an instance. It merely holds onto its parameters for - * a subsequent call to {@link #build}. - * - * @param method {@code non-null;} method to build the list for - * @param order {@code non-null;} block output order - * @param addresses {@code non-null;} address objects for each block - */ - public StdCatchBuilder(RopMethod method, int[] order, - BlockAddresses addresses) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - if (order == null) { - throw new NullPointerException("order == null"); - } - - if (addresses == null) { - throw new NullPointerException("addresses == null"); - } - - this.method = method; - this.order = order; - this.addresses = addresses; + this.method = method; + this.order = order; + this.addresses = addresses; + } + + /** {@inheritDoc} */ + @Override + public CatchTable build() { + return build(method, order, addresses); + } + + /** {@inheritDoc} */ + @Override + public boolean hasAnyCatches() { + BasicBlockList blocks = method.getBlocks(); + int size = blocks.size(); + + for (int i = 0; i < size; i++) { + BasicBlock block = blocks.get(i); + TypeList catches = block.getLastInsn().getCatches(); + if (catches.size() != 0) { + return true; + } } - /** {@inheritDoc} */ - public CatchTable build() { - return build(method, order, addresses); + return false; + } + + /** {@inheritDoc} */ + @Override + public HashSet<Type> getCatchTypes() { + HashSet<Type> result = new HashSet<Type>(20); + BasicBlockList blocks = method.getBlocks(); + int size = blocks.size(); + + for (int i = 0; i < size; i++) { + BasicBlock block = blocks.get(i); + TypeList catches = block.getLastInsn().getCatches(); + int catchSize = catches.size(); + + for (int j = 0; j < catchSize; j++) { + result.add(catches.getType(j)); + } } - /** {@inheritDoc} */ - public boolean hasAnyCatches() { - BasicBlockList blocks = method.getBlocks(); - int size = blocks.size(); + return result; + } + + /** + * Builds and returns the catch table for a given method. + * + * @param method {@code non-null;} method to build the list for + * @param order {@code non-null;} block output order + * @param addresses {@code non-null;} address objects for each block + * @return {@code non-null;} the constructed table + */ + public static CatchTable build(RopMethod method, int[] order, BlockAddresses addresses) { + int len = order.length; + BasicBlockList blocks = method.getBlocks(); + ArrayList<CatchTable.Entry> resultList = new ArrayList<CatchTable.Entry>(len); + CatchHandlerList currentHandlers = CatchHandlerList.EMPTY; + BasicBlock currentStartBlock = null; + BasicBlock currentEndBlock = null; + + for (int i = 0; i < len; i++) { + BasicBlock block = blocks.labelToBlock(order[i]); + + if (!block.canThrow()) { + /* + * There is no need to concern ourselves with the + * placement of blocks that can't throw with respect + * to the blocks that *can* throw. + */ + continue; + } + + CatchHandlerList handlers = handlersFor(block, addresses); - for (int i = 0; i < size; i++) { - BasicBlock block = blocks.get(i); - TypeList catches = block.getLastInsn().getCatches(); - if (catches.size() != 0) { - return true; - } - } + if (currentHandlers.size() == 0) { + // This is the start of a new catch range. + currentStartBlock = block; + currentEndBlock = block; + currentHandlers = handlers; + continue; + } - return false; + if (currentHandlers.equals(handlers) && rangeIsValid(currentStartBlock, block, addresses)) { + /* + * The block we are looking at now has the same handlers + * as the block that started the currently open catch + * range, and adding it to the currently open range won't + * cause it to be too long. + */ + currentEndBlock = block; + continue; + } + + /* + * The block we are looking at now has incompatible handlers, + * so we need to finish off the last entry and start a new + * one. Note: We only emit an entry if it has associated handlers. + */ + if (currentHandlers.size() != 0) { + CatchTable.Entry entry = + makeEntry(currentStartBlock, currentEndBlock, currentHandlers, addresses); + resultList.add(entry); + } + + currentStartBlock = block; + currentEndBlock = block; + currentHandlers = handlers; } - /** {@inheritDoc} */ - public HashSet<Type> getCatchTypes() { - HashSet<Type> result = new HashSet<Type>(20); - BasicBlockList blocks = method.getBlocks(); - int size = blocks.size(); + if (currentHandlers.size() != 0) { + // Emit an entry for the range that was left hanging. + CatchTable.Entry entry = + makeEntry(currentStartBlock, currentEndBlock, currentHandlers, addresses); + resultList.add(entry); + } - for (int i = 0; i < size; i++) { - BasicBlock block = blocks.get(i); - TypeList catches = block.getLastInsn().getCatches(); - int catchSize = catches.size(); + // Construct the final result. - for (int j = 0; j < catchSize; j++) { - result.add(catches.getType(j)); - } - } + int resultSz = resultList.size(); - return result; + if (resultSz == 0) { + return CatchTable.EMPTY; } - /** - * Builds and returns the catch table for a given method. - * - * @param method {@code non-null;} method to build the list for - * @param order {@code non-null;} block output order - * @param addresses {@code non-null;} address objects for each block - * @return {@code non-null;} the constructed table - */ - public static CatchTable build(RopMethod method, int[] order, - BlockAddresses addresses) { - int len = order.length; - BasicBlockList blocks = method.getBlocks(); - ArrayList<CatchTable.Entry> resultList = - new ArrayList<CatchTable.Entry>(len); - CatchHandlerList currentHandlers = CatchHandlerList.EMPTY; - BasicBlock currentStartBlock = null; - BasicBlock currentEndBlock = null; - - for (int i = 0; i < len; i++) { - BasicBlock block = blocks.labelToBlock(order[i]); - - if (!block.canThrow()) { - /* - * There is no need to concern ourselves with the - * placement of blocks that can't throw with respect - * to the blocks that *can* throw. - */ - continue; - } - - CatchHandlerList handlers = handlersFor(block, addresses); - - if (currentHandlers.size() == 0) { - // This is the start of a new catch range. - currentStartBlock = block; - currentEndBlock = block; - currentHandlers = handlers; - continue; - } - - if (currentHandlers.equals(handlers) - && rangeIsValid(currentStartBlock, block, addresses)) { - /* - * The block we are looking at now has the same handlers - * as the block that started the currently open catch - * range, and adding it to the currently open range won't - * cause it to be too long. - */ - currentEndBlock = block; - continue; - } - - /* - * The block we are looking at now has incompatible handlers, - * so we need to finish off the last entry and start a new - * one. Note: We only emit an entry if it has associated handlers. - */ - if (currentHandlers.size() != 0) { - CatchTable.Entry entry = - makeEntry(currentStartBlock, currentEndBlock, - currentHandlers, addresses); - resultList.add(entry); - } - - currentStartBlock = block; - currentEndBlock = block; - currentHandlers = handlers; - } - - if (currentHandlers.size() != 0) { - // Emit an entry for the range that was left hanging. - CatchTable.Entry entry = - makeEntry(currentStartBlock, currentEndBlock, - currentHandlers, addresses); - resultList.add(entry); - } - - // Construct the final result. - - int resultSz = resultList.size(); - - if (resultSz == 0) { - return CatchTable.EMPTY; - } - - CatchTable result = new CatchTable(resultSz); - - for (int i = 0; i < resultSz; i++) { - result.set(i, resultList.get(i)); - } - - result.setImmutable(); - return result; + CatchTable result = new CatchTable(resultSz); + + for (int i = 0; i < resultSz; i++) { + result.set(i, resultList.get(i)); } - /** - * Makes the {@link CatchHandlerList} for the given basic block. - * - * @param block {@code non-null;} block to get entries for - * @param addresses {@code non-null;} address objects for each block - * @return {@code non-null;} array of entries - */ - private static CatchHandlerList handlersFor(BasicBlock block, - BlockAddresses addresses) { - IntList successors = block.getSuccessors(); - int succSize = successors.size(); - int primary = block.getPrimarySuccessor(); - TypeList catches = block.getLastInsn().getCatches(); - int catchSize = catches.size(); - - if (catchSize == 0) { - return CatchHandlerList.EMPTY; - } - - if (((primary == -1) && (succSize != catchSize)) - || ((primary != -1) && - ((succSize != (catchSize + 1)) - || (primary != successors.get(catchSize))))) { - /* - * Blocks that throw are supposed to list their primary - * successor -- if any -- last in the successors list, but - * that constraint appears to be violated here. - */ - throw new RuntimeException( - "shouldn't happen: weird successors list"); - } + result.setImmutable(); + return result; + } + + /** + * Makes the {@link CatchHandlerList} for the given basic block. + * + * @param block {@code non-null;} block to get entries for + * @param addresses {@code non-null;} address objects for each block + * @return {@code non-null;} array of entries + */ + private static CatchHandlerList handlersFor(BasicBlock block, BlockAddresses addresses) { + IntList successors = block.getSuccessors(); + int succSize = successors.size(); + int primary = block.getPrimarySuccessor(); + TypeList catches = block.getLastInsn().getCatches(); + int catchSize = catches.size(); + + if (catchSize == 0) { + return CatchHandlerList.EMPTY; + } - /* - * Reduce the effective catchSize if we spot a catch-all that - * isn't at the end. - */ - for (int i = 0; i < catchSize; i++) { - Type type = catches.getType(i); - if (type.equals(Type.OBJECT)) { - catchSize = i + 1; - break; - } - } - - CatchHandlerList result = new CatchHandlerList(catchSize); - - for (int i = 0; i < catchSize; i++) { - CstType oneType = new CstType(catches.getType(i)); - CodeAddress oneHandler = addresses.getStart(successors.get(i)); - result.set(i, oneType, oneHandler.getAddress()); - } - - result.setImmutable(); - return result; + if (((primary == -1) && (succSize != catchSize)) || ((primary != -1) + && ((succSize != (catchSize + 1)) || (primary != successors.get(catchSize))))) { + /* + * Blocks that throw are supposed to list their primary + * successor -- if any -- last in the successors list, but + * that constraint appears to be violated here. + */ + throw new RuntimeException("shouldn't happen: weird successors list"); } - /** - * Makes a {@link CatchTable#Entry} for the given block range and - * handlers. - * - * @param start {@code non-null;} the start block for the range (inclusive) - * @param end {@code non-null;} the start block for the range (also inclusive) - * @param handlers {@code non-null;} the handlers for the range - * @param addresses {@code non-null;} address objects for each block + /* + * Reduce the effective catchSize if we spot a catch-all that + * isn't at the end. */ - private static CatchTable.Entry makeEntry(BasicBlock start, - BasicBlock end, CatchHandlerList handlers, - BlockAddresses addresses) { - /* - * We start at the *last* instruction of the start block, since - * that's the instruction that can throw... - */ - CodeAddress startAddress = addresses.getLast(start); + for (int i = 0; i < catchSize; i++) { + Type type = catches.getType(i); + if (type.equals(Type.OBJECT)) { + catchSize = i + 1; + break; + } + } - // ...And we end *after* the last instruction of the end block. - CodeAddress endAddress = addresses.getEnd(end); + CatchHandlerList result = new CatchHandlerList(catchSize); - return new CatchTable.Entry(startAddress.getAddress(), - endAddress.getAddress(), handlers); + for (int i = 0; i < catchSize; i++) { + CstType oneType = new CstType(catches.getType(i)); + CodeAddress oneHandler = addresses.getStart(successors.get(i)); + result.set(i, oneType, oneHandler.getAddress()); } - /** - * Gets whether the address range for the given two blocks is valid - * for a catch handler. This is true as long as the covered range is - * under 65536 code units. - * - * @param start {@code non-null;} the start block for the range (inclusive) - * @param end {@code non-null;} the start block for the range (also inclusive) - * @param addresses {@code non-null;} address objects for each block - * @return {@code true} if the range is valid as a catch range + result.setImmutable(); + return result; + } + + /** + * Makes a {@link CatchTable#Entry} for the given block range and + * handlers. + * + * @param start {@code non-null;} the start block for the range (inclusive) + * @param end {@code non-null;} the start block for the range (also inclusive) + * @param handlers {@code non-null;} the handlers for the range + * @param addresses {@code non-null;} address objects for each block + */ + private static CatchTable.Entry makeEntry(BasicBlock start, BasicBlock end, + CatchHandlerList handlers, BlockAddresses addresses) { + /* + * We start at the *last* instruction of the start block, since + * that's the instruction that can throw... */ - private static boolean rangeIsValid(BasicBlock start, BasicBlock end, - BlockAddresses addresses) { - if (start == null) { - throw new NullPointerException("start == null"); - } + CodeAddress startAddress = addresses.getLast(start); + + // ...And we end *after* the last instruction of the end block. + CodeAddress endAddress = addresses.getEnd(end); + + return new CatchTable.Entry(startAddress.getAddress(), endAddress.getAddress(), handlers); + } + + /** + * Gets whether the address range for the given two blocks is valid + * for a catch handler. This is true as long as the covered range is + * under 65536 code units. + * + * @param start {@code non-null;} the start block for the range (inclusive) + * @param end {@code non-null;} the start block for the range (also inclusive) + * @param addresses {@code non-null;} address objects for each block + * @return {@code true} if the range is valid as a catch range + */ + private static boolean rangeIsValid(BasicBlock start, BasicBlock end, BlockAddresses addresses) { + if (start == null) { + throw new NullPointerException("start == null"); + } - if (end == null) { - throw new NullPointerException("end == null"); - } + if (end == null) { + throw new NullPointerException("end == null"); + } - // See above about selection of instructions. - int startAddress = addresses.getLast(start).getAddress(); - int endAddress = addresses.getEnd(end).getAddress(); + // See above about selection of instructions. + int startAddress = addresses.getLast(start).getAddress(); + int endAddress = addresses.getEnd(end).getAddress(); - return (endAddress - startAddress) <= MAX_CATCH_RANGE; - } + return (endAddress - startAddress) <= MAX_CATCH_RANGE; + } } diff --git a/dx/src/com/android/jack/dx/dex/code/SwitchData.java b/dx/src/com/android/jack/dx/dex/code/SwitchData.java index 0785fba..2e6c268 100644 --- a/dx/src/com/android/jack/dx/dex/code/SwitchData.java +++ b/dx/src/com/android/jack/dx/dex/code/SwitchData.java @@ -29,230 +29,229 @@ import com.android.jack.dx.util.IntList; * in either a "packed" or "sparse" form. */ public final class SwitchData extends VariableSizeInsn { - /** - * {@code non-null;} address representing the instruction that uses this - * instance - */ - private final CodeAddress user; - - /** {@code non-null;} sorted list of switch cases (keys) */ - private final IntList cases; - - /** - * {@code non-null;} corresponding list of code addresses; the branch - * target for each case - */ - private final CodeAddress[] targets; - - /** whether the output table will be packed (vs. sparse) */ - private final boolean packed; - - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - * @param user {@code non-null;} address representing the instruction that - * uses this instance - * @param cases {@code non-null;} sorted list of switch cases (keys) - * @param targets {@code non-null;} corresponding list of code addresses; the - * branch target for each case - */ - public SwitchData(SourcePosition position, CodeAddress user, - IntList cases, CodeAddress[] targets) { - super(position, RegisterSpecList.EMPTY); - - if (user == null) { - throw new NullPointerException("user == null"); - } - - if (cases == null) { - throw new NullPointerException("cases == null"); - } - - if (targets == null) { - throw new NullPointerException("targets == null"); - } - - int sz = cases.size(); - - if (sz != targets.length) { - throw new IllegalArgumentException("cases / targets mismatch"); - } - - if (sz > 65535) { - throw new IllegalArgumentException("too many cases"); - } - - this.user = user; - this.cases = cases; - this.targets = targets; - this.packed = shouldPack(cases); + /** + * {@code non-null;} address representing the instruction that uses this + * instance + */ + private final CodeAddress user; + + /** {@code non-null;} sorted list of switch cases (keys) */ + private final IntList cases; + + /** + * {@code non-null;} corresponding list of code addresses; the branch + * target for each case + */ + private final CodeAddress[] targets; + + /** whether the output table will be packed (vs. sparse) */ + private final boolean packed; + + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + * @param user {@code non-null;} address representing the instruction that + * uses this instance + * @param cases {@code non-null;} sorted list of switch cases (keys) + * @param targets {@code non-null;} corresponding list of code addresses; the + * branch target for each case + */ + public SwitchData(SourcePosition position, CodeAddress user, IntList cases, + CodeAddress[] targets) { + super(position, RegisterSpecList.EMPTY); + + if (user == null) { + throw new NullPointerException("user == null"); } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return packed ? (int) packedCodeSize(cases) : - (int) sparseCodeSize(cases); + if (cases == null) { + throw new NullPointerException("cases == null"); } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out) { - int baseAddress = user.getAddress(); - int defaultTarget = Dops.PACKED_SWITCH.getFormat().codeSize(); - int sz = targets.length; - - if (packed) { - int firstCase = (sz == 0) ? 0 : cases.get(0); - int lastCase = (sz == 0) ? 0 : cases.get(sz - 1); - int outSz = lastCase - firstCase + 1; - - out.writeShort(Opcodes.PACKED_SWITCH_PAYLOAD); - out.writeShort(outSz); - out.writeInt(firstCase); - - int caseAt = 0; - for (int i = 0; i < outSz; i++) { - int outCase = firstCase + i; - int oneCase = cases.get(caseAt); - int relTarget; - - if (oneCase > outCase) { - relTarget = defaultTarget; - } else { - relTarget = targets[caseAt].getAddress() - baseAddress; - caseAt++; - } - - out.writeInt(relTarget); - } - } else { - out.writeShort(Opcodes.SPARSE_SWITCH_PAYLOAD); - out.writeShort(sz); - - for (int i = 0; i < sz; i++) { - out.writeInt(cases.get(i)); - } - - for (int i = 0; i < sz; i++) { - int relTarget = targets[i].getAddress() - baseAddress; - out.writeInt(relTarget); - } - } + if (targets == null) { + throw new NullPointerException("targets == null"); } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new SwitchData(getPosition(), user, cases, targets); - } + int sz = cases.size(); - /** - * Returns whether or not this instance's data will be output as packed. - * - * @return {@code true} iff the data is to be packed - */ - public boolean isPacked() { - return packed; + if (sz != targets.length) { + throw new IllegalArgumentException("cases / targets mismatch"); } - /** {@inheritDoc} */ - @Override - protected String argString() { - StringBuffer sb = new StringBuffer(100); - - int sz = targets.length; - for (int i = 0; i < sz; i++) { - sb.append("\n "); - sb.append(cases.get(i)); - sb.append(": "); - sb.append(targets[i]); - } - - return sb.toString(); + if (sz > 65535) { + throw new IllegalArgumentException("too many cases"); } - /** {@inheritDoc} */ - @Override - protected String listingString0(boolean noteIndices) { - int baseAddress = user.getAddress(); - StringBuffer sb = new StringBuffer(100); - int sz = targets.length; - - sb.append(packed ? "packed" : "sparse"); - sb.append("-switch-payload // for switch @ "); - sb.append(Hex.u2(baseAddress)); - - for (int i = 0; i < sz; i++) { - int absTarget = targets[i].getAddress(); - int relTarget = absTarget - baseAddress; - sb.append("\n "); - sb.append(cases.get(i)); - sb.append(": "); - sb.append(Hex.u4(absTarget)); - sb.append(" // "); - sb.append(Hex.s4(relTarget)); + this.user = user; + this.cases = cases; + this.targets = targets; + this.packed = shouldPack(cases); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return packed ? (int) packedCodeSize(cases) : (int) sparseCodeSize(cases); + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out) { + int baseAddress = user.getAddress(); + int defaultTarget = Dops.PACKED_SWITCH.getFormat().codeSize(); + int sz = targets.length; + + if (packed) { + int firstCase = (sz == 0) ? 0 : cases.get(0); + int lastCase = (sz == 0) ? 0 : cases.get(sz - 1); + int outSz = lastCase - firstCase + 1; + + out.writeShort(Opcodes.PACKED_SWITCH_PAYLOAD); + out.writeShort(outSz); + out.writeInt(firstCase); + + int caseAt = 0; + for (int i = 0; i < outSz; i++) { + int outCase = firstCase + i; + int oneCase = cases.get(caseAt); + int relTarget; + + if (oneCase > outCase) { + relTarget = defaultTarget; + } else { + relTarget = targets[caseAt].getAddress() - baseAddress; + caseAt++; } - return sb.toString(); - } + out.writeInt(relTarget); + } + } else { + out.writeShort(Opcodes.SPARSE_SWITCH_PAYLOAD); + out.writeShort(sz); - /** - * Gets the size of a packed table for the given cases, in 16-bit code - * units. - * - * @param cases {@code non-null;} sorted list of cases - * @return {@code >= -1;} the packed table size or {@code -1} if the - * cases couldn't possibly be represented as a packed table - */ - private static long packedCodeSize(IntList cases) { - int sz = cases.size(); - long low = cases.get(0); - long high = cases.get(sz - 1); - long result = ((high - low + 1)) * 2 + 4; + for (int i = 0; i < sz; i++) { + out.writeInt(cases.get(i)); + } - return (result <= 0x7fffffff) ? result : -1; + for (int i = 0; i < sz; i++) { + int relTarget = targets[i].getAddress() - baseAddress; + out.writeInt(relTarget); + } + } + } + + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new SwitchData(getPosition(), user, cases, targets); + } + + /** + * Returns whether or not this instance's data will be output as packed. + * + * @return {@code true} iff the data is to be packed + */ + public boolean isPacked() { + return packed; + } + + /** {@inheritDoc} */ + @Override + protected String argString() { + StringBuffer sb = new StringBuffer(100); + + int sz = targets.length; + for (int i = 0; i < sz; i++) { + sb.append("\n "); + sb.append(cases.get(i)); + sb.append(": "); + sb.append(targets[i]); } - /** - * Gets the size of a sparse table for the given cases, in 16-bit code - * units. - * - * @param cases {@code non-null;} sorted list of cases - * @return {@code > 0;} the sparse table size - */ - private static long sparseCodeSize(IntList cases) { - int sz = cases.size(); - - return (sz * 4L) + 2; + return sb.toString(); + } + + /** {@inheritDoc} */ + @Override + protected String listingString0(boolean noteIndices) { + int baseAddress = user.getAddress(); + StringBuffer sb = new StringBuffer(100); + int sz = targets.length; + + sb.append(packed ? "packed" : "sparse"); + sb.append("-switch-payload // for switch @ "); + sb.append(Hex.u2(baseAddress)); + + for (int i = 0; i < sz; i++) { + int absTarget = targets[i].getAddress(); + int relTarget = absTarget - baseAddress; + sb.append("\n "); + sb.append(cases.get(i)); + sb.append(": "); + sb.append(Hex.u4(absTarget)); + sb.append(" // "); + sb.append(Hex.s4(relTarget)); } - /** - * Determines whether the given list of cases warrant being packed. - * - * @param cases {@code non-null;} sorted list of cases - * @return {@code true} iff the table encoding the cases - * should be packed - */ - private static boolean shouldPack(IntList cases) { - int sz = cases.size(); + return sb.toString(); + } + + /** + * Gets the size of a packed table for the given cases, in 16-bit code + * units. + * + * @param cases {@code non-null;} sorted list of cases + * @return {@code >= -1;} the packed table size or {@code -1} if the + * cases couldn't possibly be represented as a packed table + */ + private static long packedCodeSize(IntList cases) { + int sz = cases.size(); + long low = cases.get(0); + long high = cases.get(sz - 1); + long result = ((high - low + 1)) * 2 + 4; + + return (result <= 0x7fffffff) ? result : -1; + } + + /** + * Gets the size of a sparse table for the given cases, in 16-bit code + * units. + * + * @param cases {@code non-null;} sorted list of cases + * @return {@code > 0;} the sparse table size + */ + private static long sparseCodeSize(IntList cases) { + int sz = cases.size(); + + return (sz * 4L) + 2; + } + + /** + * Determines whether the given list of cases warrant being packed. + * + * @param cases {@code non-null;} sorted list of cases + * @return {@code true} iff the table encoding the cases + * should be packed + */ + private static boolean shouldPack(IntList cases) { + int sz = cases.size(); + + if (sz < 2) { + return true; + } - if (sz < 2) { - return true; - } + long packedSize = packedCodeSize(cases); + long sparseSize = sparseCodeSize(cases); - long packedSize = packedCodeSize(cases); - long sparseSize = sparseCodeSize(cases); - - /* - * We pick the packed representation if it is possible and - * would be as small or smaller than 5/4 of the sparse - * representation. That is, we accept some size overhead on - * the packed representation, since that format is faster to - * execute at runtime. - */ - return (packedSize >= 0) && (packedSize <= ((sparseSize * 5) / 4)); - } + /* + * We pick the packed representation if it is possible and + * would be as small or smaller than 5/4 of the sparse + * representation. That is, we accept some size overhead on + * the packed representation, since that format is faster to + * execute at runtime. + */ + return (packedSize >= 0) && (packedSize <= ((sparseSize * 5) / 4)); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/TargetInsn.java b/dx/src/com/android/jack/dx/dex/code/TargetInsn.java index c957867..b97025d 100644 --- a/dx/src/com/android/jack/dx/dex/code/TargetInsn.java +++ b/dx/src/com/android/jack/dx/dex/code/TargetInsn.java @@ -23,110 +23,110 @@ import com.android.jack.dx.rop.code.SourcePosition; * Instruction which has a single branch target. */ public final class TargetInsn extends FixedSizeInsn { - /** {@code non-null;} the branch target */ - private CodeAddress target; - - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}), and the target is initially - * {@code null}. - * - * @param opcode the opcode; one of the constants from {@link Dops} - * @param position {@code non-null;} source position - * @param registers {@code non-null;} register list, including a - * result register if appropriate (that is, registers may be either - * ins or outs) - * @param target {@code non-null;} the branch target - */ - public TargetInsn(Dop opcode, SourcePosition position, - RegisterSpecList registers, CodeAddress target) { - super(opcode, position, registers); - - if (target == null) { - throw new NullPointerException("target == null"); - } - - this.target = target; + /** {@code non-null;} the branch target */ + private CodeAddress target; + + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}), and the target is initially + * {@code null}. + * + * @param opcode the opcode; one of the constants from {@link Dops} + * @param position {@code non-null;} source position + * @param registers {@code non-null;} register list, including a + * result register if appropriate (that is, registers may be either + * ins or outs) + * @param target {@code non-null;} the branch target + */ + public TargetInsn(Dop opcode, SourcePosition position, RegisterSpecList registers, + CodeAddress target) { + super(opcode, position, registers); + + if (target == null) { + throw new NullPointerException("target == null"); } - /** {@inheritDoc} */ - @Override - public DalvInsn withOpcode(Dop opcode) { - return new TargetInsn(opcode, getPosition(), getRegisters(), target); + this.target = target; + } + + /** {@inheritDoc} */ + @Override + public DalvInsn withOpcode(Dop opcode) { + return new TargetInsn(opcode, getPosition(), getRegisters(), target); + } + + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisters(RegisterSpecList registers) { + return new TargetInsn(getOpcode(), getPosition(), registers, target); + } + + /** + * Returns an instance that is just like this one, except that its + * opcode has the opposite sense (as a test; e.g. a + * {@code lt} test becomes a {@code ge}), and its branch + * target is replaced by the one given, and all set-once values + * associated with the class (such as its address) are reset. + * + * @param target {@code non-null;} the new branch target + * @return {@code non-null;} an appropriately-constructed instance + */ + public TargetInsn withNewTargetAndReversed(CodeAddress target) { + Dop opcode = getOpcode().getOppositeTest(); + + return new TargetInsn(opcode, getPosition(), getRegisters(), target); + } + + /** + * Gets the unique branch target of this instruction. + * + * @return {@code non-null;} the branch target + */ + public CodeAddress getTarget() { + return target; + } + + /** + * Gets the target address of this instruction. This is only valid + * to call if the target instruction has been assigned an address, + * and it is merely a convenient shorthand for + * {@code getTarget().getAddress()}. + * + * @return {@code >= 0;} the target address + */ + public int getTargetAddress() { + return target.getAddress(); + } + + /** + * Gets the branch offset of this instruction. This is only valid to + * call if both this and the target instruction each has been assigned + * an address, and it is merely a convenient shorthand for + * {@code getTargetAddress() - getAddress()}. + * + * @return the branch offset + */ + public int getTargetOffset() { + return target.getAddress() - getAddress(); + } + + /** + * Returns whether the target offset is known. + * + * @return {@code true} if the target offset is known or + * {@code false} if not + */ + public boolean hasTargetOffset() { + return hasAddress() && target.hasAddress(); + } + + /** {@inheritDoc} */ + @Override + protected String argString() { + if (target == null) { + return "????"; } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisters(RegisterSpecList registers) { - return new TargetInsn(getOpcode(), getPosition(), registers, target); - } - - /** - * Returns an instance that is just like this one, except that its - * opcode has the opposite sense (as a test; e.g. a - * {@code lt} test becomes a {@code ge}), and its branch - * target is replaced by the one given, and all set-once values - * associated with the class (such as its address) are reset. - * - * @param target {@code non-null;} the new branch target - * @return {@code non-null;} an appropriately-constructed instance - */ - public TargetInsn withNewTargetAndReversed(CodeAddress target) { - Dop opcode = getOpcode().getOppositeTest(); - - return new TargetInsn(opcode, getPosition(), getRegisters(), target); - } - - /** - * Gets the unique branch target of this instruction. - * - * @return {@code non-null;} the branch target - */ - public CodeAddress getTarget() { - return target; - } - - /** - * Gets the target address of this instruction. This is only valid - * to call if the target instruction has been assigned an address, - * and it is merely a convenient shorthand for - * {@code getTarget().getAddress()}. - * - * @return {@code >= 0;} the target address - */ - public int getTargetAddress() { - return target.getAddress(); - } - - /** - * Gets the branch offset of this instruction. This is only valid to - * call if both this and the target instruction each has been assigned - * an address, and it is merely a convenient shorthand for - * {@code getTargetAddress() - getAddress()}. - * - * @return the branch offset - */ - public int getTargetOffset() { - return target.getAddress() - getAddress(); - } - - /** - * Returns whether the target offset is known. - * - * @return {@code true} if the target offset is known or - * {@code false} if not - */ - public boolean hasTargetOffset() { - return hasAddress() && target.hasAddress(); - } - - /** {@inheritDoc} */ - @Override - protected String argString() { - if (target == null) { - return "????"; - } - - return target.identifierString(); - } + return target.identifierString(); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/VariableSizeInsn.java b/dx/src/com/android/jack/dx/dex/code/VariableSizeInsn.java index 3110629..d3e5ba5 100644 --- a/dx/src/com/android/jack/dx/dex/code/VariableSizeInsn.java +++ b/dx/src/com/android/jack/dx/dex/code/VariableSizeInsn.java @@ -23,27 +23,26 @@ import com.android.jack.dx.rop.code.SourcePosition; * Pseudo-instruction base class for variable-sized instructions. */ public abstract class VariableSizeInsn extends DalvInsn { - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - * @param registers {@code non-null;} source registers - */ - public VariableSizeInsn(SourcePosition position, - RegisterSpecList registers) { - super(Dops.SPECIAL_FORMAT, position, registers); - } + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + * @param registers {@code non-null;} source registers + */ + public VariableSizeInsn(SourcePosition position, RegisterSpecList registers) { + super(Dops.SPECIAL_FORMAT, position, registers); + } - /** {@inheritDoc} */ - @Override - public final DalvInsn withOpcode(Dop opcode) { - throw new RuntimeException("unsupported"); - } + /** {@inheritDoc} */ + @Override + public final DalvInsn withOpcode(Dop opcode) { + throw new RuntimeException("unsupported"); + } - /** {@inheritDoc} */ - @Override - public final DalvInsn withRegisterOffset(int delta) { - return withRegisters(getRegisters().withOffset(delta)); - } + /** {@inheritDoc} */ + @Override + public final DalvInsn withRegisterOffset(int delta) { + return withRegisters(getRegisters().withOffset(delta)); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/ZeroSizeInsn.java b/dx/src/com/android/jack/dx/dex/code/ZeroSizeInsn.java index d9a7f1b..4e1de10 100644 --- a/dx/src/com/android/jack/dx/dex/code/ZeroSizeInsn.java +++ b/dx/src/com/android/jack/dx/dex/code/ZeroSizeInsn.java @@ -26,37 +26,37 @@ import com.android.jack.dx.util.AnnotatedOutput; * about the code they are adjacent to. */ public abstract class ZeroSizeInsn extends DalvInsn { - /** - * Constructs an instance. The output address of this instance is initially - * unknown ({@code -1}). - * - * @param position {@code non-null;} source position - */ - public ZeroSizeInsn(SourcePosition position) { - super(Dops.SPECIAL_FORMAT, position, RegisterSpecList.EMPTY); - } + /** + * Constructs an instance. The output address of this instance is initially + * unknown ({@code -1}). + * + * @param position {@code non-null;} source position + */ + public ZeroSizeInsn(SourcePosition position) { + super(Dops.SPECIAL_FORMAT, position, RegisterSpecList.EMPTY); + } - /** {@inheritDoc} */ - @Override - public final int codeSize() { - return 0; - } + /** {@inheritDoc} */ + @Override + public final int codeSize() { + return 0; + } - /** {@inheritDoc} */ - @Override - public final void writeTo(AnnotatedOutput out) { - // Nothing to do here, for this class. - } + /** {@inheritDoc} */ + @Override + public final void writeTo(AnnotatedOutput out) { + // Nothing to do here, for this class. + } - /** {@inheritDoc} */ - @Override - public final DalvInsn withOpcode(Dop opcode) { - throw new RuntimeException("unsupported"); - } + /** {@inheritDoc} */ + @Override + public final DalvInsn withOpcode(Dop opcode) { + throw new RuntimeException("unsupported"); + } - /** {@inheritDoc} */ - @Override - public DalvInsn withRegisterOffset(int delta) { - return withRegisters(getRegisters().withOffset(delta)); - } + /** {@inheritDoc} */ + @Override + public DalvInsn withRegisterOffset(int delta) { + return withRegisters(getRegisters().withOffset(delta)); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form10t.java b/dx/src/com/android/jack/dx/dex/code/form/Form10t.java index 6f27986..cc484ba 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form10t.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form10t.java @@ -26,61 +26,60 @@ import com.android.jack.dx.util.AnnotatedOutput; * for details. */ public final class Form10t extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form10t(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form10t(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form10t() { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - return branchString(insn); - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form10t() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - return branchComment(insn); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + return branchString(insn); + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 1; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + return branchComment(insn); + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - if (!((insn instanceof TargetInsn) && - (insn.getRegisters().size() == 0))) { - return false; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 1; + } - TargetInsn ti = (TargetInsn) insn; - return ti.hasTargetOffset() ? branchFits(ti) : true; + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!((insn instanceof TargetInsn) && (insn.getRegisters().size() == 0))) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean branchFits(TargetInsn insn) { - int offset = insn.getTargetOffset(); + TargetInsn ti = (TargetInsn) insn; + return ti.hasTargetOffset() ? branchFits(ti) : true; + } - // Note: A zero offset would fit, but it is prohibited by the spec. - return (offset != 0) && signedFitsInByte(offset); - } + /** {@inheritDoc} */ + @Override + public boolean branchFits(TargetInsn insn) { + int offset = insn.getTargetOffset(); - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - int offset = ((TargetInsn) insn).getTargetOffset(); + // Note: A zero offset would fit, but it is prohibited by the spec. + return (offset != 0) && signedFitsInByte(offset); + } - write(out, opcodeUnit(insn, (offset & 0xff))); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + int offset = ((TargetInsn) insn).getTargetOffset(); + + write(out, opcodeUnit(insn, (offset & 0xff))); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form10x.java b/dx/src/com/android/jack/dx/dex/code/form/Form10x.java index 785f0fa..30dbd74 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form10x.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form10x.java @@ -26,47 +26,46 @@ import com.android.jack.dx.util.AnnotatedOutput; * for details. */ public final class Form10x extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form10x(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form10x(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form10x() { - // This space intentionally left blank. - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form10x() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - // This format has no arguments. - return ""; - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + // This format has no arguments. + return ""; + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - // This format has no comment. - return ""; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + // This format has no comment. + return ""; + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 1; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 1; + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - return (insn instanceof SimpleInsn) && - (insn.getRegisters().size() == 0); - } + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + return (insn instanceof SimpleInsn) && (insn.getRegisters().size() == 0); + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - write(out, opcodeUnit(insn, 0)); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + write(out, opcodeUnit(insn, 0)); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form11n.java b/dx/src/com/android/jack/dx/dex/code/form/Form11n.java index c30357e..7c4cf49 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form11n.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form11n.java @@ -31,80 +31,77 @@ import java.util.BitSet; * for details. */ public final class Form11n extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form11n(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form11n() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form11n(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form11n() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + + return regs.get(0).regString() + ", " + literalBitsString(value); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + return literalBitsComment(value, 4); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 1; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + + if (!((insn instanceof CstInsn) && (regs.size() == 1) + && unsignedFitsInNibble(regs.get(0).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); - return regs.get(0).regString() + ", " + literalBitsString(value); + if (!(cst instanceof CstLiteralBits)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); - return literalBitsComment(value, 4); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 1; - } + CstLiteralBits cb = (CstLiteralBits) cst; - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); + return cb.fitsInInt() && signedFitsInNibble(cb.getIntBits()); + } - if (!((insn instanceof CstInsn) && - (regs.size() == 1) && - unsignedFitsInNibble(regs.get(0).getReg()))) { - return false; - } + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(1); - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); + bits.set(0, unsignedFitsInNibble(regs.get(0).getReg())); + return bits; + } - if (!(cst instanceof CstLiteralBits)) { - return false; - } - - CstLiteralBits cb = (CstLiteralBits) cst; - - return cb.fitsInInt() && signedFitsInNibble(cb.getIntBits()); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int value = ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(1); - - bits.set(0, unsignedFitsInNibble(regs.get(0).getReg())); - return bits; - } - - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int value = - ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - - write(out, - opcodeUnit(insn, makeByte(regs.get(0).getReg(), value & 0xf))); - } + write(out, opcodeUnit(insn, makeByte(regs.get(0).getReg(), value & 0xf))); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form11x.java b/dx/src/com/android/jack/dx/dex/code/form/Form11x.java index a000202..24f35fa 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form11x.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form11x.java @@ -29,60 +29,59 @@ import java.util.BitSet; * for details. */ public final class Form11x extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form11x(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form11x(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form11x() { - // This space intentionally left blank. - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form11x() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString(); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString(); + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - // This format has no comment. - return ""; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + // This format has no comment. + return ""; + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 1; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 1; + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return (insn instanceof SimpleInsn) && - (regs.size() == 1) && - unsignedFitsInByte(regs.get(0).getReg()); - } + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return (insn instanceof SimpleInsn) && (regs.size() == 1) + && unsignedFitsInByte(regs.get(0).getReg()); + } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(1); + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(1); - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - return bits; - } + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + return bits; + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - write(out, opcodeUnit(insn, regs.get(0).getReg())); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + write(out, opcodeUnit(insn, regs.get(0).getReg())); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form12x.java b/dx/src/com/android/jack/dx/dex/code/form/Form12x.java index 4c93d27..e0aabe4 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form12x.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form12x.java @@ -30,133 +30,129 @@ import java.util.BitSet; * for details. */ public final class Form12x extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form12x(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form12x(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form12x() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int sz = regs.size(); + + /* + * The (sz - 2) and (sz - 1) below makes this code work for + * both the two- and three-register ops. (See "case 3" in + * isCompatible(), below.) */ - private Form12x() { - // This space intentionally left blank. - } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int sz = regs.size(); +return regs.get(sz - 2).regString() + ", " + regs.get(sz - 1).regString(); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + // This format has no comment. + return ""; + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 1; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!(insn instanceof SimpleInsn)) { + return false; + } + RegisterSpecList regs = insn.getRegisters(); + RegisterSpec rs1; + RegisterSpec rs2; + + switch (regs.size()) { + case 2: { + rs1 = regs.get(0); + rs2 = regs.get(1); + break; + } + case 3: { /* - * The (sz - 2) and (sz - 1) below makes this code work for - * both the two- and three-register ops. (See "case 3" in - * isCompatible(), below.) + * This format is allowed for ops that are effectively + * 3-arg but where the first two args are identical. */ - - return regs.get(sz - 2).regString() + ", " + - regs.get(sz - 1).regString(); - } - - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - // This format has no comment. - return ""; - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 1; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - if (!(insn instanceof SimpleInsn)) { - return false; - } - - RegisterSpecList regs = insn.getRegisters(); - RegisterSpec rs1; - RegisterSpec rs2; - - switch (regs.size()) { - case 2: { - rs1 = regs.get(0); - rs2 = regs.get(1); - break; - } - case 3: { - /* - * This format is allowed for ops that are effectively - * 3-arg but where the first two args are identical. - */ - rs1 = regs.get(1); - rs2 = regs.get(2); - if (rs1.getReg() != regs.get(0).getReg()) { - return false; - } - break; - } - default: { - return false; - } + rs1 = regs.get(1); + rs2 = regs.get(2); + if (rs1.getReg() != regs.get(0).getReg()) { + return false; } - - return unsignedFitsInNibble(rs1.getReg()) && - unsignedFitsInNibble(rs2.getReg()); + break; + } + default: { + return false; + } } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(2); - int r0 = regs.get(0).getReg(); - int r1 = regs.get(1).getReg(); - - switch (regs.size()) { - case 2: { - bits.set(0, unsignedFitsInNibble(r0)); - bits.set(1, unsignedFitsInNibble(r1)); - break; - } - case 3: { - if (r0 != r1) { - bits.set(0, false); - bits.set(1, false); - } else { - boolean dstRegComp = unsignedFitsInNibble(r1); - bits.set(0, dstRegComp); - bits.set(1, dstRegComp); - } - - bits.set(2, unsignedFitsInNibble(regs.get(2).getReg())); - break; - } - default: { - throw new AssertionError(); - } + return unsignedFitsInNibble(rs1.getReg()) && unsignedFitsInNibble(rs2.getReg()); + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(2); + int r0 = regs.get(0).getReg(); + int r1 = regs.get(1).getReg(); + + switch (regs.size()) { + case 2: { + bits.set(0, unsignedFitsInNibble(r0)); + bits.set(1, unsignedFitsInNibble(r1)); + break; + } + case 3: { + if (r0 != r1) { + bits.set(0, false); + bits.set(1, false); + } else { + boolean dstRegComp = unsignedFitsInNibble(r1); + bits.set(0, dstRegComp); + bits.set(1, dstRegComp); } - return bits; + bits.set(2, unsignedFitsInNibble(regs.get(2).getReg())); + break; + } + default: { + throw new AssertionError(); + } } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int sz = regs.size(); + return bits; + } - /* - * The (sz - 2) and (sz - 1) below makes this code work for - * both the two- and three-register ops. (See "case 3" in - * isCompatible(), above.) - */ + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int sz = regs.size(); - write(out, opcodeUnit(insn, - makeByte(regs.get(sz - 2).getReg(), - regs.get(sz - 1).getReg()))); - } + /* + * The (sz - 2) and (sz - 1) below makes this code work for + * both the two- and three-register ops. (See "case 3" in + * isCompatible(), above.) + */ + +write(out, opcodeUnit(insn, makeByte(regs.get(sz - 2).getReg(), regs.get(sz - 1).getReg()))); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form20t.java b/dx/src/com/android/jack/dx/dex/code/form/Form20t.java index e05042a..8044c02 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form20t.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form20t.java @@ -26,61 +26,60 @@ import com.android.jack.dx.util.AnnotatedOutput; * for details. */ public final class Form20t extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form20t(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form20t(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form20t() { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - return branchString(insn); - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form20t() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - return branchComment(insn); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + return branchString(insn); + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + return branchComment(insn); + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - if (!((insn instanceof TargetInsn) && - (insn.getRegisters().size() == 0))) { - return false; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } - TargetInsn ti = (TargetInsn) insn; - return ti.hasTargetOffset() ? branchFits(ti) : true; + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!((insn instanceof TargetInsn) && (insn.getRegisters().size() == 0))) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean branchFits(TargetInsn insn) { - int offset = insn.getTargetOffset(); + TargetInsn ti = (TargetInsn) insn; + return ti.hasTargetOffset() ? branchFits(ti) : true; + } - // Note: A zero offset would fit, but it is prohibited by the spec. - return (offset != 0) && signedFitsInShort(offset); - } + /** {@inheritDoc} */ + @Override + public boolean branchFits(TargetInsn insn) { + int offset = insn.getTargetOffset(); - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - int offset = ((TargetInsn) insn).getTargetOffset(); + // Note: A zero offset would fit, but it is prohibited by the spec. + return (offset != 0) && signedFitsInShort(offset); + } - write(out, opcodeUnit(insn, 0), (short) offset); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + int offset = ((TargetInsn) insn).getTargetOffset(); + + write(out, opcodeUnit(insn, 0), (short) offset); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form21c.java b/dx/src/com/android/jack/dx/dex/code/form/Form21c.java index 90415f1..fa77e8f 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form21c.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form21c.java @@ -34,116 +34,112 @@ import java.util.BitSet; * for details. */ public final class Form21c extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form21c(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form21c() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form21c(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form21c() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + if (noteIndices) { + return cstComment(insn); + } else { + return ""; } - - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!(insn instanceof CstInsn)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - if (noteIndices) { - return cstComment(insn); - } else { - return ""; + RegisterSpecList regs = insn.getRegisters(); + RegisterSpec reg; + + switch (regs.size()) { + case 1: { + reg = regs.get(0); + break; + } + case 2: { + /* + * This format is allowed for ops that are effectively + * 2-arg but where the two args are identical. + */ + reg = regs.get(0); + if (reg.getReg() != regs.get(1).getReg()) { + return false; } + break; + } + default: { + return false; + } } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; + if (!unsignedFitsInByte(reg.getReg())) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - if (!(insn instanceof CstInsn)) { - return false; - } - - RegisterSpecList regs = insn.getRegisters(); - RegisterSpec reg; - - switch (regs.size()) { - case 1: { - reg = regs.get(0); - break; - } - case 2: { - /* - * This format is allowed for ops that are effectively - * 2-arg but where the two args are identical. - */ - reg = regs.get(0); - if (reg.getReg() != regs.get(1).getReg()) { - return false; - } - break; - } - default: { - return false; - } - } - - if (!unsignedFitsInByte(reg.getReg())) { - return false; - } - - CstInsn ci = (CstInsn) insn; - int cpi = ci.getIndex(); - Constant cst = ci.getConstant(); + CstInsn ci = (CstInsn) insn; + int cpi = ci.getIndex(); + Constant cst = ci.getConstant(); - if (! unsignedFitsInShort(cpi)) { - return false; - } - - return (cst instanceof CstType) || - (cst instanceof CstFieldRef) || - (cst instanceof CstString); + if (!unsignedFitsInShort(cpi)) { + return false; } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int sz = regs.size(); - BitSet bits = new BitSet(sz); - boolean compat = unsignedFitsInByte(regs.get(0).getReg()); - - if (sz == 1) { - bits.set(0, compat); - } else { - if (regs.get(0).getReg() == regs.get(1).getReg()) { - bits.set(0, compat); - bits.set(1, compat); - } - } - - return bits; + return (cst instanceof CstType) || (cst instanceof CstFieldRef) || (cst instanceof CstString); + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int sz = regs.size(); + BitSet bits = new BitSet(sz); + boolean compat = unsignedFitsInByte(regs.get(0).getReg()); + + if (sz == 1) { + bits.set(0, compat); + } else { + if (regs.get(0).getReg() == regs.get(1).getReg()) { + bits.set(0, compat); + bits.set(1, compat); + } } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int cpi = ((CstInsn) insn).getIndex(); + return bits; + } - write(out, - opcodeUnit(insn, regs.get(0).getReg()), - (short) cpi); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int cpi = ((CstInsn) insn).getIndex(); + + write(out, opcodeUnit(insn, regs.get(0).getReg()), (short) cpi); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form21h.java b/dx/src/com/android/jack/dx/dex/code/form/Form21h.java index 4a57f0a..585fa53 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form21h.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form21h.java @@ -31,96 +31,93 @@ import java.util.BitSet; * for details. */ public final class Form21h extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form21h(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form21h() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form21h(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form21h() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + + return regs.get(0).regString() + ", " + literalBitsString(value); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + + return literalBitsComment(value, (regs.get(0).getCategory() == 1) ? 32 : 64); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + if (!((insn instanceof CstInsn) && (regs.size() == 1) + && unsignedFitsInByte(regs.get(0).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); - return regs.get(0).regString() + ", " + literalBitsString(value); + if (!(cst instanceof CstLiteralBits)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + CstLiteralBits cb = (CstLiteralBits) cst; - return - literalBitsComment(value, - (regs.get(0).getCategory() == 1) ? 32 : 64); + // Where the high bits are depends on the category of the target. + if (regs.get(0).getCategory() == 1) { + int bits = cb.getIntBits(); + return ((bits & 0xffff) == 0); + } else { + long bits = cb.getLongBits(); + return ((bits & 0xffffffffffffL) == 0); } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - if (!((insn instanceof CstInsn) && - (regs.size() == 1) && - unsignedFitsInByte(regs.get(0).getReg()))) { - return false; - } - - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); - - if (!(cst instanceof CstLiteralBits)) { - return false; - } - - CstLiteralBits cb = (CstLiteralBits) cst; - - // Where the high bits are depends on the category of the target. - if (regs.get(0).getCategory() == 1) { - int bits = cb.getIntBits(); - return ((bits & 0xffff) == 0); - } else { - long bits = cb.getLongBits(); - return ((bits & 0xffffffffffffL) == 0); - } + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(1); + + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + return bits; + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits cb = (CstLiteralBits) ((CstInsn) insn).getConstant(); + short bits; + + // Where the high bits are depends on the category of the target. + if (regs.get(0).getCategory() == 1) { + bits = (short) (cb.getIntBits() >>> 16); + } else { + bits = (short) (cb.getLongBits() >>> 48); } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(1); - - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - return bits; - } - - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits cb = (CstLiteralBits) ((CstInsn) insn).getConstant(); - short bits; - - // Where the high bits are depends on the category of the target. - if (regs.get(0).getCategory() == 1) { - bits = (short) (cb.getIntBits() >>> 16); - } else { - bits = (short) (cb.getLongBits() >>> 48); - } - - write(out, opcodeUnit(insn, regs.get(0).getReg()), bits); - } + write(out, opcodeUnit(insn, regs.get(0).getReg()), bits); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form21s.java b/dx/src/com/android/jack/dx/dex/code/form/Form21s.java index 57dd12a..0852541 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form21s.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form21s.java @@ -31,80 +31,76 @@ import java.util.BitSet; * for details. */ public final class Form21s extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form21s(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form21s() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form21s(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form21s() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + + return regs.get(0).regString() + ", " + literalBitsString(value); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + return literalBitsComment(value, 16); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + if (!((insn instanceof CstInsn) && (regs.size() == 1) + && unsignedFitsInByte(regs.get(0).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); - return regs.get(0).regString() + ", " + literalBitsString(value); + if (!(cst instanceof CstLiteralBits)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); - return literalBitsComment(value, 16); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - if (!((insn instanceof CstInsn) && - (regs.size() == 1) && - unsignedFitsInByte(regs.get(0).getReg()))) { - return false; - } - - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); + CstLiteralBits cb = (CstLiteralBits) cst; - if (!(cst instanceof CstLiteralBits)) { - return false; - } + return cb.fitsInInt() && signedFitsInShort(cb.getIntBits()); + } - CstLiteralBits cb = (CstLiteralBits) cst; + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(1); - return cb.fitsInInt() && signedFitsInShort(cb.getIntBits()); - } - - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(1); + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + return bits; + } - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - return bits; - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int value = ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int value = - ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - - write(out, - opcodeUnit(insn, regs.get(0).getReg()), - (short) value); - } + write(out, opcodeUnit(insn, regs.get(0).getReg()), (short) value); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form21t.java b/dx/src/com/android/jack/dx/dex/code/form/Form21t.java index 5836a5e..955eced 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form21t.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form21t.java @@ -29,78 +29,75 @@ import java.util.BitSet; * for details. */ public final class Form21t extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form21t(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form21t() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form21t(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form21t() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + branchString(insn); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + return branchComment(insn); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + + if (!((insn instanceof TargetInsn) && (regs.size() == 1) + && unsignedFitsInByte(regs.get(0).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + branchString(insn); - } - - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - return branchComment(insn); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - - if (!((insn instanceof TargetInsn) && - (regs.size() == 1) && - unsignedFitsInByte(regs.get(0).getReg()))) { - return false; - } - - TargetInsn ti = (TargetInsn) insn; - return ti.hasTargetOffset() ? branchFits(ti) : true; - } - - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(1); - - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - return bits; - } - - /** {@inheritDoc} */ - @Override - public boolean branchFits(TargetInsn insn) { - int offset = insn.getTargetOffset(); - - // Note: A zero offset would fit, but it is prohibited by the spec. - return (offset != 0) && signedFitsInShort(offset); - } - - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int offset = ((TargetInsn) insn).getTargetOffset(); - - write(out, - opcodeUnit(insn, regs.get(0).getReg()), - (short) offset); - } + TargetInsn ti = (TargetInsn) insn; + return ti.hasTargetOffset() ? branchFits(ti) : true; + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(1); + + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + return bits; + } + + /** {@inheritDoc} */ + @Override + public boolean branchFits(TargetInsn insn) { + int offset = insn.getTargetOffset(); + + // Note: A zero offset would fit, but it is prohibited by the spec. + return (offset != 0) && signedFitsInShort(offset); + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int offset = ((TargetInsn) insn).getTargetOffset(); + + write(out, opcodeUnit(insn, regs.get(0).getReg()), (short) offset); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form22b.java b/dx/src/com/android/jack/dx/dex/code/form/Form22b.java index a09b6c6..fcae319 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form22b.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form22b.java @@ -31,83 +31,79 @@ import java.util.BitSet; * for details. */ public final class Form22b extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form22b(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form22b() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form22b(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form22b() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + + return regs.get(0).regString() + ", " + regs.get(1).regString() + ", " + + literalBitsString(value); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + return literalBitsComment(value, 8); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + if (!((insn instanceof CstInsn) && (regs.size() == 2) + && unsignedFitsInByte(regs.get(0).getReg()) && unsignedFitsInByte(regs.get(1).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); - return regs.get(0).regString() + ", " + regs.get(1).regString() + - ", " + literalBitsString(value); + if (!(cst instanceof CstLiteralBits)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); - return literalBitsComment(value, 8); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - if (!((insn instanceof CstInsn) && - (regs.size() == 2) && - unsignedFitsInByte(regs.get(0).getReg()) && - unsignedFitsInByte(regs.get(1).getReg()))) { - return false; - } - - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); + CstLiteralBits cb = (CstLiteralBits) cst; - if (!(cst instanceof CstLiteralBits)) { - return false; - } + return cb.fitsInInt() && signedFitsInByte(cb.getIntBits()); + } - CstLiteralBits cb = (CstLiteralBits) cst; + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(2); - return cb.fitsInInt() && signedFitsInByte(cb.getIntBits()); - } - - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(2); + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + bits.set(1, unsignedFitsInByte(regs.get(1).getReg())); + return bits; + } - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - bits.set(1, unsignedFitsInByte(regs.get(1).getReg())); - return bits; - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int value = ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int value = - ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - - write(out, - opcodeUnit(insn, regs.get(0).getReg()), - codeUnit(regs.get(1).getReg(), value & 0xff)); - } + write(out, opcodeUnit(insn, regs.get(0).getReg()), + codeUnit(regs.get(1).getReg(), value & 0xff)); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form22c.java b/dx/src/com/android/jack/dx/dex/code/form/Form22c.java index d170e06..fdf51c4 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form22c.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form22c.java @@ -32,84 +32,78 @@ import java.util.BitSet; * for details. */ public final class Form22c extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form22c(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form22c() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form22c(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form22c() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + regs.get(1).regString() + ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + if (noteIndices) { + return cstComment(insn); + } else { + return ""; } - - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + regs.get(1).regString() + - ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + if (!((insn instanceof CstInsn) && (regs.size() == 2) + && unsignedFitsInNibble(regs.get(0).getReg()) + && unsignedFitsInNibble(regs.get(1).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - if (noteIndices) { - return cstComment(insn); - } else { - return ""; - } - } + CstInsn ci = (CstInsn) insn; + int cpi = ci.getIndex(); - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; + if (!unsignedFitsInShort(cpi)) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - if (!((insn instanceof CstInsn) && - (regs.size() == 2) && - unsignedFitsInNibble(regs.get(0).getReg()) && - unsignedFitsInNibble(regs.get(1).getReg()))) { - return false; - } - - CstInsn ci = (CstInsn) insn; - int cpi = ci.getIndex(); - - if (! unsignedFitsInShort(cpi)) { - return false; - } - - Constant cst = ci.getConstant(); - return (cst instanceof CstType) || - (cst instanceof CstFieldRef); - } - - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(2); - - bits.set(0, unsignedFitsInNibble(regs.get(0).getReg())); - bits.set(1, unsignedFitsInNibble(regs.get(1).getReg())); - return bits; - } - - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int cpi = ((CstInsn) insn).getIndex(); - - write(out, - opcodeUnit(insn, - makeByte(regs.get(0).getReg(), regs.get(1).getReg())), - (short) cpi); - } + Constant cst = ci.getConstant(); + return (cst instanceof CstType) || (cst instanceof CstFieldRef); + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(2); + + bits.set(0, unsignedFitsInNibble(regs.get(0).getReg())); + bits.set(1, unsignedFitsInNibble(regs.get(1).getReg())); + return bits; + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int cpi = ((CstInsn) insn).getIndex(); + + write(out, opcodeUnit(insn, makeByte(regs.get(0).getReg(), regs.get(1).getReg())), (short) cpi); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form22s.java b/dx/src/com/android/jack/dx/dex/code/form/Form22s.java index bbd66a3..1fcbd76 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form22s.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form22s.java @@ -31,84 +31,80 @@ import java.util.BitSet; * for details. */ public final class Form22s extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form22s(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form22s() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form22s(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form22s() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + + return regs.get(0).regString() + ", " + regs.get(1).regString() + ", " + + literalBitsString(value); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + return literalBitsComment(value, 16); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + if (!((insn instanceof CstInsn) && (regs.size() == 2) + && unsignedFitsInNibble(regs.get(0).getReg()) + && unsignedFitsInNibble(regs.get(1).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); - return regs.get(0).regString() + ", " + regs.get(1).regString() - + ", " + literalBitsString(value); + if (!(cst instanceof CstLiteralBits)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); - return literalBitsComment(value, 16); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } + CstLiteralBits cb = (CstLiteralBits) cst; - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - if (!((insn instanceof CstInsn) && - (regs.size() == 2) && - unsignedFitsInNibble(regs.get(0).getReg()) && - unsignedFitsInNibble(regs.get(1).getReg()))) { - return false; - } + return cb.fitsInInt() && signedFitsInShort(cb.getIntBits()); + } - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(2); - if (!(cst instanceof CstLiteralBits)) { - return false; - } + bits.set(0, unsignedFitsInNibble(regs.get(0).getReg())); + bits.set(1, unsignedFitsInNibble(regs.get(1).getReg())); + return bits; + } - CstLiteralBits cb = (CstLiteralBits) cst; - - return cb.fitsInInt() && signedFitsInShort(cb.getIntBits()); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int value = ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(2); - - bits.set(0, unsignedFitsInNibble(regs.get(0).getReg())); - bits.set(1, unsignedFitsInNibble(regs.get(1).getReg())); - return bits; - } - - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int value = - ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - - write(out, - opcodeUnit(insn, - makeByte(regs.get(0).getReg(), regs.get(1).getReg())), - (short) value); - } + write(out, opcodeUnit(insn, makeByte(regs.get(0).getReg(), regs.get(1).getReg())), + (short) value); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form22t.java b/dx/src/com/android/jack/dx/dex/code/form/Form22t.java index 3e9cfdb..66dbde8 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form22t.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form22t.java @@ -29,82 +29,78 @@ import java.util.BitSet; * for details. */ public final class Form22t extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form22t(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form22t() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form22t(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form22t() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + regs.get(1).regString() + ", " + branchString(insn); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + return branchComment(insn); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + + if (!((insn instanceof TargetInsn) && (regs.size() == 2) + && unsignedFitsInNibble(regs.get(0).getReg()) + && unsignedFitsInNibble(regs.get(1).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + regs.get(1).regString() + - ", " + branchString(insn); - } - - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - return branchComment(insn); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - - if (!((insn instanceof TargetInsn) && - (regs.size() == 2) && - unsignedFitsInNibble(regs.get(0).getReg()) && - unsignedFitsInNibble(regs.get(1).getReg()))) { - return false; - } - - TargetInsn ti = (TargetInsn) insn; - return ti.hasTargetOffset() ? branchFits(ti) : true; - } - - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(2); - - bits.set(0, unsignedFitsInNibble(regs.get(0).getReg())); - bits.set(1, unsignedFitsInNibble(regs.get(1).getReg())); - return bits; - } - - /** {@inheritDoc} */ - @Override - public boolean branchFits(TargetInsn insn) { - int offset = insn.getTargetOffset(); - - // Note: A zero offset would fit, but it is prohibited by the spec. - return (offset != 0) && signedFitsInShort(offset); - } - - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int offset = ((TargetInsn) insn).getTargetOffset(); - - write(out, - opcodeUnit(insn, - makeByte(regs.get(0).getReg(), regs.get(1).getReg())), - (short) offset); - } + TargetInsn ti = (TargetInsn) insn; + return ti.hasTargetOffset() ? branchFits(ti) : true; + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(2); + + bits.set(0, unsignedFitsInNibble(regs.get(0).getReg())); + bits.set(1, unsignedFitsInNibble(regs.get(1).getReg())); + return bits; + } + + /** {@inheritDoc} */ + @Override + public boolean branchFits(TargetInsn insn) { + int offset = insn.getTargetOffset(); + + // Note: A zero offset would fit, but it is prohibited by the spec. + return (offset != 0) && signedFitsInShort(offset); + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int offset = ((TargetInsn) insn).getTargetOffset(); + + write(out, opcodeUnit(insn, makeByte(regs.get(0).getReg(), regs.get(1).getReg())), + (short) offset); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form22x.java b/dx/src/com/android/jack/dx/dex/code/form/Form22x.java index f9e4d9c..e505566 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form22x.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form22x.java @@ -29,65 +29,61 @@ import java.util.BitSet; * for details. */ public final class Form22x extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form22x(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form22x(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form22x() { - // This space intentionally left blank. - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form22x() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + regs.get(1).regString(); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + regs.get(1).regString(); + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - // This format has no comment. - return ""; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + // This format has no comment. + return ""; + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); - return (insn instanceof SimpleInsn) && - (regs.size() == 2) && - unsignedFitsInByte(regs.get(0).getReg()) && - unsignedFitsInShort(regs.get(1).getReg()); - } + return (insn instanceof SimpleInsn) && (regs.size() == 2) + && unsignedFitsInByte(regs.get(0).getReg()) && unsignedFitsInShort(regs.get(1).getReg()); + } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(2); + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(2); - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - bits.set(1, unsignedFitsInShort(regs.get(1).getReg())); - return bits; - } + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + bits.set(1, unsignedFitsInShort(regs.get(1).getReg())); + return bits; + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - write(out, - opcodeUnit(insn, regs.get(0).getReg()), - (short) regs.get(1).getReg()); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + write(out, opcodeUnit(insn, regs.get(0).getReg()), (short) regs.get(1).getReg()); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form23x.java b/dx/src/com/android/jack/dx/dex/code/form/Form23x.java index 3261788..40dc9c0 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form23x.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form23x.java @@ -29,68 +29,65 @@ import java.util.BitSet; * for details. */ public final class Form23x extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form23x(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form23x(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form23x() { - // This space intentionally left blank. - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form23x() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + regs.get(1).regString() + - ", " + regs.get(2).regString(); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + regs.get(1).regString() + ", " + + regs.get(2).regString(); + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - // This format has no comment. - return ""; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + // This format has no comment. + return ""; + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 2; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 2; + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); - return (insn instanceof SimpleInsn) && - (regs.size() == 3) && - unsignedFitsInByte(regs.get(0).getReg()) && - unsignedFitsInByte(regs.get(1).getReg()) && - unsignedFitsInByte(regs.get(2).getReg()); - } + return (insn instanceof SimpleInsn) && (regs.size() == 3) + && unsignedFitsInByte(regs.get(0).getReg()) && unsignedFitsInByte(regs.get(1).getReg()) + && unsignedFitsInByte(regs.get(2).getReg()); + } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(3); + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(3); - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - bits.set(1, unsignedFitsInByte(regs.get(1).getReg())); - bits.set(2, unsignedFitsInByte(regs.get(2).getReg())); - return bits; - } + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + bits.set(1, unsignedFitsInByte(regs.get(1).getReg())); + bits.set(2, unsignedFitsInByte(regs.get(2).getReg())); + return bits; + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - write(out, - opcodeUnit(insn, regs.get(0).getReg()), - codeUnit(regs.get(1).getReg(), regs.get(2).getReg())); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + write(out, opcodeUnit(insn, regs.get(0).getReg()), + codeUnit(regs.get(1).getReg(), regs.get(2).getReg())); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form30t.java b/dx/src/com/android/jack/dx/dex/code/form/Form30t.java index 743f806..579253f 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form30t.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form30t.java @@ -26,57 +26,56 @@ import com.android.jack.dx.util.AnnotatedOutput; * for details. */ public final class Form30t extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form30t(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form30t(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form30t() { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - return branchString(insn); - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form30t() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - return branchComment(insn); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + return branchString(insn); + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 3; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + return branchComment(insn); + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - if (!((insn instanceof TargetInsn) && - (insn.getRegisters().size() == 0))) { - return false; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 3; + } - return true; + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!((insn instanceof TargetInsn) && (insn.getRegisters().size() == 0))) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean branchFits(TargetInsn insn) { - return true; - } + return true; + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - int offset = ((TargetInsn) insn).getTargetOffset(); + /** {@inheritDoc} */ + @Override + public boolean branchFits(TargetInsn insn) { + return true; + } - write(out, opcodeUnit(insn, 0), offset); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + int offset = ((TargetInsn) insn).getTargetOffset(); + + write(out, opcodeUnit(insn, 0), offset); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form31c.java b/dx/src/com/android/jack/dx/dex/code/form/Form31c.java index 12e5f14..cd4877a 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form31c.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form31c.java @@ -34,109 +34,107 @@ import java.util.BitSet; * for details. */ public final class Form31c extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form31c(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form31c() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form31c(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form31c() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + if (noteIndices) { + return cstComment(insn); + } else { + return ""; } - - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 3; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!(insn instanceof CstInsn)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - if (noteIndices) { - return cstComment(insn); - } else { - return ""; + RegisterSpecList regs = insn.getRegisters(); + RegisterSpec reg; + + switch (regs.size()) { + case 1: { + reg = regs.get(0); + break; + } + case 2: { + /* + * This format is allowed for ops that are effectively + * 2-arg but where the two args are identical. + */ + reg = regs.get(0); + if (reg.getReg() != regs.get(1).getReg()) { + return false; } + break; + } + default: { + return false; + } } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 3; + if (!unsignedFitsInByte(reg.getReg())) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - if (!(insn instanceof CstInsn)) { - return false; - } - - RegisterSpecList regs = insn.getRegisters(); - RegisterSpec reg; - - switch (regs.size()) { - case 1: { - reg = regs.get(0); - break; - } - case 2: { - /* - * This format is allowed for ops that are effectively - * 2-arg but where the two args are identical. - */ - reg = regs.get(0); - if (reg.getReg() != regs.get(1).getReg()) { - return false; - } - break; - } - default: { - return false; - } - } - - if (!unsignedFitsInByte(reg.getReg())) { - return false; - } - - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); - - return (cst instanceof CstType) || - (cst instanceof CstFieldRef) || - (cst instanceof CstString); + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); + + return (cst instanceof CstType) || (cst instanceof CstFieldRef) || (cst instanceof CstString); + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int sz = regs.size(); + BitSet bits = new BitSet(sz); + boolean compat = unsignedFitsInByte(regs.get(0).getReg()); + + if (sz == 1) { + bits.set(0, compat); + } else { + if (regs.get(0).getReg() == regs.get(1).getReg()) { + bits.set(0, compat); + bits.set(1, compat); + } } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int sz = regs.size(); - BitSet bits = new BitSet(sz); - boolean compat = unsignedFitsInByte(regs.get(0).getReg()); - - if (sz == 1) { - bits.set(0, compat); - } else { - if (regs.get(0).getReg() == regs.get(1).getReg()) { - bits.set(0, compat); - bits.set(1, compat); - } - } - - return bits; - } + return bits; + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int cpi = ((CstInsn) insn).getIndex(); + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int cpi = ((CstInsn) insn).getIndex(); - write(out, opcodeUnit(insn, regs.get(0).getReg()), cpi); - } + write(out, opcodeUnit(insn, regs.get(0).getReg()), cpi); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form31i.java b/dx/src/com/android/jack/dx/dex/code/form/Form31i.java index 8e45f18..4a10162 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form31i.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form31i.java @@ -31,76 +31,74 @@ import java.util.BitSet; * for details. */ public final class Form31i extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form31i(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form31i() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form31i(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form31i() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + + return regs.get(0).regString() + ", " + literalBitsString(value); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + return literalBitsComment(value, 32); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 3; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + if (!((insn instanceof CstInsn) && (regs.size() == 1) + && unsignedFitsInByte(regs.get(0).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); - return regs.get(0).regString() + ", " + literalBitsString(value); + if (!(cst instanceof CstLiteralBits)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); - return literalBitsComment(value, 32); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 3; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - if (!((insn instanceof CstInsn) && - (regs.size() == 1) && - unsignedFitsInByte(regs.get(0).getReg()))) { - return false; - } - - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); + return ((CstLiteralBits) cst).fitsInInt(); + } - if (!(cst instanceof CstLiteralBits)) { - return false; - } + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(1); - return ((CstLiteralBits) cst).fitsInInt(); - } - - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(1); + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + return bits; + } - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - return bits; - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int value = ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int value = - ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits(); - - write(out, opcodeUnit(insn, regs.get(0).getReg()), value); - } + write(out, opcodeUnit(insn, regs.get(0).getReg()), value); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form31t.java b/dx/src/com/android/jack/dx/dex/code/form/Form31t.java index 8573615..603fd18 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form31t.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form31t.java @@ -29,72 +29,71 @@ import java.util.BitSet; * for details. */ public final class Form31t extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form31t(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form31t() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form31t(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form31t() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + branchString(insn); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + return branchComment(insn); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 3; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + + if (!((insn instanceof TargetInsn) && (regs.size() == 1) + && unsignedFitsInByte(regs.get(0).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + branchString(insn); - } - - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - return branchComment(insn); - } - - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 3; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - - if (!((insn instanceof TargetInsn) && - (regs.size() == 1) && - unsignedFitsInByte(regs.get(0).getReg()))) { - return false; - } - - return true; - } - - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(1); - - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - return bits; - } - - /** {@inheritDoc} */ - @Override - public boolean branchFits(TargetInsn insn) { - return true; - } - - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int offset = ((TargetInsn) insn).getTargetOffset(); - - write(out, opcodeUnit(insn, regs.get(0).getReg()), offset); - } + return true; + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(1); + + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + return bits; + } + + /** {@inheritDoc} */ + @Override + public boolean branchFits(TargetInsn insn) { + return true; + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int offset = ((TargetInsn) insn).getTargetOffset(); + + write(out, opcodeUnit(insn, regs.get(0).getReg()), offset); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form32x.java b/dx/src/com/android/jack/dx/dex/code/form/Form32x.java index 2f856ce..07d18a2 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form32x.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form32x.java @@ -29,66 +29,61 @@ import java.util.BitSet; * for details. */ public final class Form32x extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form32x(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form32x(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form32x() { - // This space intentionally left blank. - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form32x() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return regs.get(0).regString() + ", " + regs.get(1).regString(); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return regs.get(0).regString() + ", " + regs.get(1).regString(); + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - // This format has no comment. - return ""; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + // This format has no comment. + return ""; + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 3; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 3; + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - return (insn instanceof SimpleInsn) && - (regs.size() == 2) && - unsignedFitsInShort(regs.get(0).getReg()) && - unsignedFitsInShort(regs.get(1).getReg()); - } + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + return (insn instanceof SimpleInsn) && (regs.size() == 2) + && unsignedFitsInShort(regs.get(0).getReg()) && unsignedFitsInShort(regs.get(1).getReg()); + } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(2); + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(2); - bits.set(0, unsignedFitsInShort(regs.get(0).getReg())); - bits.set(1, unsignedFitsInShort(regs.get(1).getReg())); - return bits; - } + bits.set(0, unsignedFitsInShort(regs.get(0).getReg())); + bits.set(1, unsignedFitsInShort(regs.get(1).getReg())); + return bits; + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); - write(out, - opcodeUnit(insn, 0), - (short) regs.get(0).getReg(), - (short) regs.get(1).getReg()); - } + write(out, opcodeUnit(insn, 0), (short) regs.get(0).getReg(), (short) regs.get(1).getReg()); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form35c.java b/dx/src/com/android/jack/dx/dex/code/form/Form35c.java index df277ef..9fd6a03 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form35c.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form35c.java @@ -34,178 +34,172 @@ import java.util.BitSet; * for details. */ public final class Form35c extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form35c(); - - /** Maximal number of operands */ - private static final int MAX_NUM_OPS = 5; - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form35c() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form35c(); + + /** Maximal number of operands */ + private static final int MAX_NUM_OPS = 5; + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form35c() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = explicitize(insn.getRegisters()); + return regListString(regs) + ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + if (noteIndices) { + return cstComment(insn); + } else { + return ""; } - - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = explicitize(insn.getRegisters()); - return regListString(regs) + ", " + cstString(insn); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 3; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!(insn instanceof CstInsn)) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - if (noteIndices) { - return cstComment(insn); - } else { - return ""; - } - } + CstInsn ci = (CstInsn) insn; + int cpi = ci.getIndex(); - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 3; + if (!unsignedFitsInShort(cpi)) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - if (!(insn instanceof CstInsn)) { - return false; - } - - CstInsn ci = (CstInsn) insn; - int cpi = ci.getIndex(); - - if (! unsignedFitsInShort(cpi)) { - return false; - } - - Constant cst = ci.getConstant(); - if (!((cst instanceof CstMethodRef) || - (cst instanceof CstType))) { - return false; - } + Constant cst = ci.getConstant(); + if (!((cst instanceof CstMethodRef) || (cst instanceof CstType))) { + return false; + } - RegisterSpecList regs = ci.getRegisters(); - return (wordCount(regs) >= 0); + RegisterSpecList regs = ci.getRegisters(); + return (wordCount(regs) >= 0); + } + + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int sz = regs.size(); + BitSet bits = new BitSet(sz); + + for (int i = 0; i < sz; i++) { + RegisterSpec reg = regs.get(i); + /* + * The check below adds (category - 1) to the register, to + * account for the fact that the second half of a + * category-2 register has to be represented explicitly in + * the result. + */ + bits.set(i, unsignedFitsInNibble(reg.getReg() + reg.getCategory() - 1)); } - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int sz = regs.size(); - BitSet bits = new BitSet(sz); - - for (int i = 0; i < sz; i++) { - RegisterSpec reg = regs.get(i); - /* - * The check below adds (category - 1) to the register, to - * account for the fact that the second half of a - * category-2 register has to be represented explicitly in - * the result. - */ - bits.set(i, unsignedFitsInNibble(reg.getReg() + - reg.getCategory() - 1)); - } - - return bits; + return bits; + } + + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + int cpi = ((CstInsn) insn).getIndex(); + RegisterSpecList regs = explicitize(insn.getRegisters()); + int sz = regs.size(); + int r0 = (sz > 0) ? regs.get(0).getReg() : 0; + int r1 = (sz > 1) ? regs.get(1).getReg() : 0; + int r2 = (sz > 2) ? regs.get(2).getReg() : 0; + int r3 = (sz > 3) ? regs.get(3).getReg() : 0; + int r4 = (sz > 4) ? regs.get(4).getReg() : 0; + + write(out, opcodeUnit(insn, makeByte(r4, sz)), // encode the fifth operand here + (short) cpi, codeUnit(r0, r1, r2, r3)); + } + + /** + * Gets the number of words required for the given register list, where + * category-2 values count as two words. Return {@code -1} if the + * list requires more than five words or contains registers that need + * more than a nibble to identify them. + * + * @param regs {@code non-null;} the register list in question + * @return {@code >= -1;} the number of words required, or {@code -1} + * if the list couldn't possibly fit in this format + */ + private static int wordCount(RegisterSpecList regs) { + int sz = regs.size(); + + if (sz > MAX_NUM_OPS) { + // It can't possibly fit. + return -1; } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - int cpi = ((CstInsn) insn).getIndex(); - RegisterSpecList regs = explicitize(insn.getRegisters()); - int sz = regs.size(); - int r0 = (sz > 0) ? regs.get(0).getReg() : 0; - int r1 = (sz > 1) ? regs.get(1).getReg() : 0; - int r2 = (sz > 2) ? regs.get(2).getReg() : 0; - int r3 = (sz > 3) ? regs.get(3).getReg() : 0; - int r4 = (sz > 4) ? regs.get(4).getReg() : 0; - - write(out, - opcodeUnit(insn, - makeByte(r4, sz)), // encode the fifth operand here - (short) cpi, - codeUnit(r0, r1, r2, r3)); + int result = 0; + + for (int i = 0; i < sz; i++) { + RegisterSpec one = regs.get(i); + result += one.getCategory(); + /* + * The check below adds (category - 1) to the register, to + * account for the fact that the second half of a + * category-2 register has to be represented explicitly in + * the result. + */ + if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) { + return -1; + } } - /** - * Gets the number of words required for the given register list, where - * category-2 values count as two words. Return {@code -1} if the - * list requires more than five words or contains registers that need - * more than a nibble to identify them. - * - * @param regs {@code non-null;} the register list in question - * @return {@code >= -1;} the number of words required, or {@code -1} - * if the list couldn't possibly fit in this format - */ - private static int wordCount(RegisterSpecList regs) { - int sz = regs.size(); - - if (sz > MAX_NUM_OPS) { - // It can't possibly fit. - return -1; - } - - int result = 0; - - for (int i = 0; i < sz; i++) { - RegisterSpec one = regs.get(i); - result += one.getCategory(); - /* - * The check below adds (category - 1) to the register, to - * account for the fact that the second half of a - * category-2 register has to be represented explicitly in - * the result. - */ - if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) { - return -1; - } - } - - return (result <= MAX_NUM_OPS) ? result : -1; + return (result <= MAX_NUM_OPS) ? result : -1; + } + + /** + * Returns a register list which is equivalent to the given one, + * except that it splits category-2 registers into two explicit + * entries. This returns the original list if no modification is + * required + * + * @param orig {@code non-null;} the original list + * @return {@code non-null;} the list with the described transformation + */ + private static RegisterSpecList explicitize(RegisterSpecList orig) { + int wordCount = wordCount(orig); + int sz = orig.size(); + + if (wordCount == sz) { + return orig; } - /** - * Returns a register list which is equivalent to the given one, - * except that it splits category-2 registers into two explicit - * entries. This returns the original list if no modification is - * required - * - * @param orig {@code non-null;} the original list - * @return {@code non-null;} the list with the described transformation - */ - private static RegisterSpecList explicitize(RegisterSpecList orig) { - int wordCount = wordCount(orig); - int sz = orig.size(); - - if (wordCount == sz) { - return orig; - } - - RegisterSpecList result = new RegisterSpecList(wordCount); - int wordAt = 0; - - for (int i = 0; i < sz; i++) { - RegisterSpec one = orig.get(i); - result.set(wordAt, one); - if (one.getCategory() == 2) { - result.set(wordAt + 1, - RegisterSpec.make(one.getReg() + 1, Type.VOID)); - wordAt += 2; - } else { - wordAt++; - } - } - - result.setImmutable(); - return result; + RegisterSpecList result = new RegisterSpecList(wordCount); + int wordAt = 0; + + for (int i = 0; i < sz; i++) { + RegisterSpec one = orig.get(i); + result.set(wordAt, one); + if (one.getCategory() == 2) { + result.set(wordAt + 1, RegisterSpec.make(one.getReg() + 1, Type.VOID)); + wordAt += 2; + } else { + wordAt++; + } } + + result.setImmutable(); + return result; + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form3rc.java b/dx/src/com/android/jack/dx/dex/code/form/Form3rc.java index 35adc7a..ec0ca88 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form3rc.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form3rc.java @@ -30,77 +30,72 @@ import com.android.jack.dx.util.AnnotatedOutput; * for details. */ public final class Form3rc extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form3rc(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form3rc(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form3rc() { - // This space intentionally left blank. - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form3rc() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - return regRangeString(insn.getRegisters()) + ", " + - cstString(insn); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + return regRangeString(insn.getRegisters()) + ", " + cstString(insn); + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - if (noteIndices) { - return cstComment(insn); - } else { - return ""; - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + if (noteIndices) { + return cstComment(insn); + } else { + return ""; } + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 3; - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 3; + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - if (!(insn instanceof CstInsn)) { - return false; - } + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + if (!(insn instanceof CstInsn)) { + return false; + } - CstInsn ci = (CstInsn) insn; - int cpi = ci.getIndex(); - Constant cst = ci.getConstant(); + CstInsn ci = (CstInsn) insn; + int cpi = ci.getIndex(); + Constant cst = ci.getConstant(); - if (! unsignedFitsInShort(cpi)) { - return false; - } + if (!unsignedFitsInShort(cpi)) { + return false; + } - if (!((cst instanceof CstMethodRef) || - (cst instanceof CstType))) { - return false; - } + if (!((cst instanceof CstMethodRef) || (cst instanceof CstType))) { + return false; + } - RegisterSpecList regs = ci.getRegisters(); - int sz = regs.size(); + RegisterSpecList regs = ci.getRegisters(); - return (regs.size() == 0) || - (isRegListSequential(regs) && - unsignedFitsInShort(regs.get(0).getReg()) && - unsignedFitsInByte(regs.getWordCount())); - } + return (regs.size() == 0) || (isRegListSequential(regs) + && unsignedFitsInShort(regs.get(0).getReg()) && unsignedFitsInByte(regs.getWordCount())); + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - int cpi = ((CstInsn) insn).getIndex(); - int firstReg = (regs.size() == 0) ? 0 : regs.get(0).getReg(); - int count = regs.getWordCount(); + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + int cpi = ((CstInsn) insn).getIndex(); + int firstReg = (regs.size() == 0) ? 0 : regs.get(0).getReg(); + int count = regs.getWordCount(); - write(out, opcodeUnit(insn, count), (short) cpi, (short) firstReg); - } + write(out, opcodeUnit(insn, count), (short) cpi, (short) firstReg); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/Form51l.java b/dx/src/com/android/jack/dx/dex/code/form/Form51l.java index 9dd086f..5555149 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/Form51l.java +++ b/dx/src/com/android/jack/dx/dex/code/form/Form51l.java @@ -32,72 +32,70 @@ import java.util.BitSet; * for details. */ public final class Form51l extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new Form51l(); - - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private Form51l() { - // This space intentionally left blank. + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new Form51l(); + + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private Form51l() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + + return regs.get(0).regString() + ", " + literalBitsString(value); + } + + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + return literalBitsComment(value, 64); + } + + /** {@inheritDoc} */ + @Override + public int codeSize() { + return 5; + } + + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + if (!((insn instanceof CstInsn) && (regs.size() == 1) + && unsignedFitsInByte(regs.get(0).getReg()))) { + return false; } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); + CstInsn ci = (CstInsn) insn; + Constant cst = ci.getConstant(); - return regs.get(0).regString() + ", " + literalBitsString(value); - } - - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant(); - return literalBitsComment(value, 64); - } + return (cst instanceof CstLiteral64); + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - return 5; - } - - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - if (!((insn instanceof CstInsn) && - (regs.size() == 1) && - unsignedFitsInByte(regs.get(0).getReg()))) { - return false; - } + /** {@inheritDoc} */ + @Override + public BitSet compatibleRegs(DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + BitSet bits = new BitSet(1); - CstInsn ci = (CstInsn) insn; - Constant cst = ci.getConstant(); + bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); + return bits; + } - return (cst instanceof CstLiteral64); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + RegisterSpecList regs = insn.getRegisters(); + long value = ((CstLiteral64) ((CstInsn) insn).getConstant()).getLongBits(); - /** {@inheritDoc} */ - @Override - public BitSet compatibleRegs(DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - BitSet bits = new BitSet(1); - - bits.set(0, unsignedFitsInByte(regs.get(0).getReg())); - return bits; - } - - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - RegisterSpecList regs = insn.getRegisters(); - long value = - ((CstLiteral64) ((CstInsn) insn).getConstant()).getLongBits(); - - write(out, opcodeUnit(insn, regs.get(0).getReg()), value); - } + write(out, opcodeUnit(insn, regs.get(0).getReg()), value); + } } diff --git a/dx/src/com/android/jack/dx/dex/code/form/SpecialFormat.java b/dx/src/com/android/jack/dx/dex/code/form/SpecialFormat.java index bce8a67..b0cf53a 100644 --- a/dx/src/com/android/jack/dx/dex/code/form/SpecialFormat.java +++ b/dx/src/com/android/jack/dx/dex/code/form/SpecialFormat.java @@ -29,44 +29,44 @@ import com.android.jack.dx.util.AnnotatedOutput; * always returns {@code true}. */ public final class SpecialFormat extends InsnFormat { - /** {@code non-null;} unique instance of this class */ - public static final InsnFormat THE_ONE = new SpecialFormat(); + /** {@code non-null;} unique instance of this class */ + public static final InsnFormat THE_ONE = new SpecialFormat(); - /** - * Constructs an instance. This class is not publicly - * instantiable. Use {@link #THE_ONE}. - */ - private SpecialFormat() { - // This space intentionally left blank. - } + /** + * Constructs an instance. This class is not publicly + * instantiable. Use {@link #THE_ONE}. + */ + private SpecialFormat() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public String insnArgString(DalvInsn insn) { - throw new RuntimeException("unsupported"); - } + /** {@inheritDoc} */ + @Override + public String insnArgString(DalvInsn insn) { + throw new RuntimeException("unsupported"); + } - /** {@inheritDoc} */ - @Override - public String insnCommentString(DalvInsn insn, boolean noteIndices) { - throw new RuntimeException("unsupported"); - } + /** {@inheritDoc} */ + @Override + public String insnCommentString(DalvInsn insn, boolean noteIndices) { + throw new RuntimeException("unsupported"); + } - /** {@inheritDoc} */ - @Override - public int codeSize() { - throw new RuntimeException("unsupported"); - } + /** {@inheritDoc} */ + @Override + public int codeSize() { + throw new RuntimeException("unsupported"); + } - /** {@inheritDoc} */ - @Override - public boolean isCompatible(DalvInsn insn) { - return true; - } + /** {@inheritDoc} */ + @Override + public boolean isCompatible(DalvInsn insn) { + return true; + } - /** {@inheritDoc} */ - @Override - public void writeTo(AnnotatedOutput out, DalvInsn insn) { - throw new RuntimeException("unsupported"); - } + /** {@inheritDoc} */ + @Override + public void writeTo(AnnotatedOutput out, DalvInsn insn) { + throw new RuntimeException("unsupported"); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/AnnotationItem.java b/dx/src/com/android/jack/dx/dex/file/AnnotationItem.java index c48c3d8..c7394c9 100644 --- a/dx/src/com/android/jack/dx/dex/file/AnnotationItem.java +++ b/dx/src/com/android/jack/dx/dex/file/AnnotationItem.java @@ -32,187 +32,193 @@ import java.util.Comparator; * element pairs. */ public final class AnnotationItem extends OffsettedItem { - /** annotation visibility constant: visible at build time only */ - private static final int VISIBILITY_BUILD = 0; + /** annotation visibility constant: visible at build time only */ + private static final int VISIBILITY_BUILD = 0; - /** annotation visibility constant: visible at runtime */ - private static final int VISIBILITY_RUNTIME = 1; + /** annotation visibility constant: visible at runtime */ + private static final int VISIBILITY_RUNTIME = 1; - /** annotation visibility constant: visible at runtime only to system */ - private static final int VISIBILITY_SYSTEM = 2; + /** annotation visibility constant: visible at runtime only to system */ + private static final int VISIBILITY_SYSTEM = 2; - /** the required alignment for instances of this class */ - private static final int ALIGNMENT = 1; + /** the required alignment for instances of this class */ + private static final int ALIGNMENT = 1; - /** {@code non-null;} unique instance of {@link #TypeIdSorter} */ - private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter(); + /** {@code non-null;} unique instance of {@link #TypeIdSorter} */ + private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter(); - /** {@code non-null;} the annotation to represent */ - private final Annotation annotation; + /** {@code non-null;} the annotation to represent */ + private final Annotation annotation; - /** - * {@code null-ok;} type reference for the annotation type; set during - * {@link #addContents} - */ - private TypeIdItem type; - - /** - * {@code null-ok;} encoded form, ready for writing to a file; set during - * {@link #place0} - */ - private byte[] encodedForm; - - /** - * Comparator that sorts (outer) instances by type id index. - */ - private static class TypeIdSorter implements Comparator<AnnotationItem> { - /** {@inheritDoc} */ - public int compare(AnnotationItem item1, AnnotationItem item2) { - int index1 = item1.type.getIndex(); - int index2 = item2.type.getIndex(); - - if (index1 < index2) { - return -1; - } else if (index1 > index2) { - return 1; - } - - return 0; - } - } + /** + * {@code null-ok;} type reference for the annotation type; set during + * {@link #addContents} + */ + private TypeIdItem type; - /** - * Sorts an array of instances, in place, by type id index, - * ignoring all other aspects of the elements. This is only valid - * to use after type id indices are known. - * - * @param array {@code non-null;} array to sort - */ - public static void sortByTypeIdIndex(AnnotationItem[] array) { - Arrays.sort(array, TYPE_ID_SORTER); - } - - /** - * Constructs an instance. - * - * @param annotation {@code non-null;} annotation to represent - */ - public AnnotationItem(Annotation annotation) { - /* - * The write size isn't known up-front because (the variable-lengthed) - * leb128 type is used to represent some things. - */ - super(ALIGNMENT, -1); - - if (annotation == null) { - throw new NullPointerException("annotation == null"); - } - - this.annotation = annotation; - this.type = null; - this.encodedForm = null; - } + /** + * {@code null-ok;} encoded form, ready for writing to a file; set during + * {@link #place0} + */ + private byte[] encodedForm; + /** + * Comparator that sorts (outer) instances by type id index. + */ + private static class TypeIdSorter implements Comparator<AnnotationItem> { /** {@inheritDoc} */ @Override - public ItemType itemType() { - return ItemType.TYPE_ANNOTATION_ITEM; - } + public int compare(AnnotationItem item1, AnnotationItem item2) { + int index1 = item1.type.getIndex(); + int index2 = item2.type.getIndex(); - /** {@inheritDoc} */ - @Override - public int hashCode() { - return annotation.hashCode(); - } + if (index1 < index2) { + return -1; + } else if (index1 > index2) { + return 1; + } - /** {@inheritDoc} */ - @Override - protected int compareTo0(OffsettedItem other) { - AnnotationItem otherAnnotation = (AnnotationItem) other; - - return annotation.compareTo(otherAnnotation.annotation); + return 0; } + } + + /** + * Sorts an array of instances, in place, by type id index, + * ignoring all other aspects of the elements. This is only valid + * to use after type id indices are known. + * + * @param array {@code non-null;} array to sort + */ + public static void sortByTypeIdIndex(AnnotationItem[] array) { + Arrays.sort(array, TYPE_ID_SORTER); + } + + /** + * Constructs an instance. + * + * @param annotation {@code non-null;} annotation to represent + */ + public AnnotationItem(Annotation annotation) { + /* + * The write size isn't known up-front because (the variable-lengthed) + * leb128 type is used to represent some things. + */ + super(ALIGNMENT, -1); - /** {@inheritDoc} */ - @Override - public String toHuman() { - return annotation.toHuman(); + if (annotation == null) { + throw new NullPointerException("annotation == null"); } - /** {@inheritDoc} */ - public void addContents(DexFile file) { - type = file.getTypeIds().intern(annotation.getType()); - ValueEncoder.addContents(file, annotation); + this.annotation = annotation; + this.type = null; + this.encodedForm = null; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_ANNOTATION_ITEM; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return annotation.hashCode(); + } + + /** {@inheritDoc} */ + @Override + protected int compareTo0(OffsettedItem other) { + AnnotationItem otherAnnotation = (AnnotationItem) other; + + return annotation.compareTo(otherAnnotation.annotation); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return annotation.toHuman(); + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + type = file.getTypeIds().intern(annotation.getType()); + ValueEncoder.addContents(file, annotation); + } + + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + // Encode the data and note the size. + + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out); + + encoder.writeAnnotation(annotation, false); + encodedForm = out.toByteArray(); + + // Add one for the visibility byte in front of the encoded annotation. + setWriteSize(encodedForm.length + 1); + } + + /** + * Write a (listing file) annotation for this instance to the given + * output, that consumes no bytes of output. This is for annotating + * a reference to this instance at the point of the reference. + * + * @param out {@code non-null;} where to output to + * @param prefix {@code non-null;} prefix for each line of output + */ + public void annotateTo(AnnotatedOutput out, String prefix) { + out.annotate(0, prefix + "visibility: " + annotation.getVisibility().toHuman()); + out.annotate(0, prefix + "type: " + annotation.getType().toHuman()); + + for (NameValuePair pair : annotation.getNameValuePairs()) { + CstString name = pair.getName(); + Constant value = pair.getValue(); + + out.annotate(0, prefix + name.toHuman() + ": " + ValueEncoder.constantToHuman(value)); } + } - /** {@inheritDoc} */ - @Override - protected void place0(Section addedTo, int offset) { - // Encode the data and note the size. - - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); - ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out); - - encoder.writeAnnotation(annotation, false); - encodedForm = out.toByteArray(); + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + AnnotationVisibility visibility = annotation.getVisibility(); - // Add one for the visibility byte in front of the encoded annotation. - setWriteSize(encodedForm.length + 1); + if (annotates) { + out.annotate(0, offsetString() + " annotation"); + out.annotate(1, " visibility: VISBILITY_" + visibility); } - /** - * Write a (listing file) annotation for this instance to the given - * output, that consumes no bytes of output. This is for annotating - * a reference to this instance at the point of the reference. - * - * @param out {@code non-null;} where to output to - * @param prefix {@code non-null;} prefix for each line of output - */ - public void annotateTo(AnnotatedOutput out, String prefix) { - out.annotate(0, prefix + "visibility: " + - annotation.getVisibility().toHuman()); - out.annotate(0, prefix + "type: " + annotation.getType().toHuman()); - - for (NameValuePair pair : annotation.getNameValuePairs()) { - CstString name = pair.getName(); - Constant value = pair.getValue(); - - out.annotate(0, prefix + name.toHuman() + ": " + - ValueEncoder.constantToHuman(value)); - } + switch (visibility) { + case BUILD: + out.writeByte(VISIBILITY_BUILD); + break; + case RUNTIME: + out.writeByte(VISIBILITY_RUNTIME); + break; + case SYSTEM: + out.writeByte(VISIBILITY_SYSTEM); + break; + default: { + // EMBEDDED shouldn't appear at the top level. + throw new RuntimeException("shouldn't happen"); + } } - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - boolean annotates = out.annotates(); - AnnotationVisibility visibility = annotation.getVisibility(); - - if (annotates) { - out.annotate(0, offsetString() + " annotation"); - out.annotate(1, " visibility: VISBILITY_" + visibility); - } - - switch (visibility) { - case BUILD: out.writeByte(VISIBILITY_BUILD); break; - case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break; - case SYSTEM: out.writeByte(VISIBILITY_SYSTEM); break; - default: { - // EMBEDDED shouldn't appear at the top level. - throw new RuntimeException("shouldn't happen"); - } - } - - if (annotates) { - /* - * The output is to be annotated, so redo the work previously - * done by place0(), except this time annotations will actually - * get emitted. - */ - ValueEncoder encoder = new ValueEncoder(file, out); - encoder.writeAnnotation(annotation, true); - } else { - out.write(encodedForm); - } + if (annotates) { + /* + * The output is to be annotated, so redo the work previously + * done by place0(), except this time annotations will actually + * get emitted. + */ + ValueEncoder encoder = new ValueEncoder(file, out); + encoder.writeAnnotation(annotation, true); + } else { + out.write(encodedForm); } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/AnnotationSetItem.java b/dx/src/com/android/jack/dx/dex/file/AnnotationSetItem.java index 9672023..eea7fa6 100644 --- a/dx/src/com/android/jack/dx/dex/file/AnnotationSetItem.java +++ b/dx/src/com/android/jack/dx/dex/file/AnnotationSetItem.java @@ -25,133 +25,133 @@ import com.android.jack.dx.util.Hex; * Set of annotations, where no annotation type appears more than once. */ public final class AnnotationSetItem extends OffsettedItem { - /** the required alignment for instances of this class */ - private static final int ALIGNMENT = 4; - - /** the size of an entry int the set: one {@code uint} */ - private static final int ENTRY_WRITE_SIZE = 4; - - /** {@code non-null;} the set of annotations */ - private final Annotations annotations; - - /** - * {@code non-null;} set of annotations as individual items in an array. - * <b>Note:</b> The contents have to get sorted by type id before - * writing. - */ - private final AnnotationItem[] items; - - /** - * Constructs an instance. - * - * @param annotations {@code non-null;} set of annotations - */ - public AnnotationSetItem(Annotations annotations) { - super(ALIGNMENT, writeSize(annotations)); - - this.annotations = annotations; - this.items = new AnnotationItem[annotations.size()]; - - int at = 0; - for (Annotation a : annotations.getAnnotations()) { - items[at] = new AnnotationItem(a); - at++; - } + /** the required alignment for instances of this class */ + private static final int ALIGNMENT = 4; + + /** the size of an entry int the set: one {@code uint} */ + private static final int ENTRY_WRITE_SIZE = 4; + + /** {@code non-null;} the set of annotations */ + private final Annotations annotations; + + /** + * {@code non-null;} set of annotations as individual items in an array. + * <b>Note:</b> The contents have to get sorted by type id before + * writing. + */ + private final AnnotationItem[] items; + + /** + * Constructs an instance. + * + * @param annotations {@code non-null;} set of annotations + */ + public AnnotationSetItem(Annotations annotations) { + super(ALIGNMENT, writeSize(annotations)); + + this.annotations = annotations; + this.items = new AnnotationItem[annotations.size()]; + + int at = 0; + for (Annotation a : annotations.getAnnotations()) { + items[at] = new AnnotationItem(a); + at++; } - - /** - * Gets the write size for the given set. - * - * @param annotations {@code non-null;} the set - * @return {@code > 0;} the write size - */ - private static int writeSize(Annotations annotations) { - // This includes an int size at the start of the list. - - try { - return (annotations.size() * ENTRY_WRITE_SIZE) + 4; - } catch (NullPointerException ex) { - // Elucidate the exception. - throw new NullPointerException("list == null"); - } + } + + /** + * Gets the write size for the given set. + * + * @param annotations {@code non-null;} the set + * @return {@code > 0;} the write size + */ + private static int writeSize(Annotations annotations) { + // This includes an int size at the start of the list. + + try { + return (annotations.size() * ENTRY_WRITE_SIZE) + 4; + } catch (NullPointerException ex) { + // Elucidate the exception. + throw new NullPointerException("list == null"); } - - /** - * Gets the underlying annotations of this instance - * - * @return {@code non-null;} the annotations - */ - public Annotations getAnnotations() { - return annotations; + } + + /** + * Gets the underlying annotations of this instance + * + * @return {@code non-null;} the annotations + */ + public Annotations getAnnotations() { + return annotations; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return annotations.hashCode(); + } + + /** {@inheritDoc} */ + @Override + protected int compareTo0(OffsettedItem other) { + AnnotationSetItem otherSet = (AnnotationSetItem) other; + + return annotations.compareTo(otherSet.annotations); + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_ANNOTATION_SET_ITEM; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return annotations.toString(); + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + MixedItemSection byteData = file.getByteData(); + int size = items.length; + + for (int i = 0; i < size; i++) { + items[i] = byteData.intern(items[i]); } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return annotations.hashCode(); + } + + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + // Sort the array to be in type id index order. + AnnotationItem.sortByTypeIdIndex(items); + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + int size = items.length; + + if (annotates) { + out.annotate(0, offsetString() + " annotation set"); + out.annotate(4, " size: " + Hex.u4(size)); } - /** {@inheritDoc} */ - @Override - protected int compareTo0(OffsettedItem other) { - AnnotationSetItem otherSet = (AnnotationSetItem) other; - - return annotations.compareTo(otherSet.annotations); - } - - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_ANNOTATION_SET_ITEM; - } - - /** {@inheritDoc} */ - @Override - public String toHuman() { - return annotations.toString(); - } - - /** {@inheritDoc} */ - public void addContents(DexFile file) { - MixedItemSection byteData = file.getByteData(); - int size = items.length; - - for (int i = 0; i < size; i++) { - items[i] = byteData.intern(items[i]); - } - } - - /** {@inheritDoc} */ - @Override - protected void place0(Section addedTo, int offset) { - // Sort the array to be in type id index order. - AnnotationItem.sortByTypeIdIndex(items); - } - - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - boolean annotates = out.annotates(); - int size = items.length; - - if (annotates) { - out.annotate(0, offsetString() + " annotation set"); - out.annotate(4, " size: " + Hex.u4(size)); - } - - out.writeInt(size); + out.writeInt(size); - for (int i = 0; i < size; i++) { - AnnotationItem item = items[i]; - int offset = item.getAbsoluteOffset(); + for (int i = 0; i < size; i++) { + AnnotationItem item = items[i]; + int offset = item.getAbsoluteOffset(); - if (annotates) { - out.annotate(4, " entries[" + Integer.toHexString(i) + "]: " + - Hex.u4(offset)); - items[i].annotateTo(out, " "); - } + if (annotates) { + out.annotate(4, " entries[" + Integer.toHexString(i) + "]: " + Hex.u4(offset)); + items[i].annotateTo(out, " "); + } - out.writeInt(offset); - } + out.writeInt(offset); } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/AnnotationSetRefItem.java b/dx/src/com/android/jack/dx/dex/file/AnnotationSetRefItem.java index a78dafc..2759ebd 100644 --- a/dx/src/com/android/jack/dx/dex/file/AnnotationSetRefItem.java +++ b/dx/src/com/android/jack/dx/dex/file/AnnotationSetRefItem.java @@ -23,58 +23,59 @@ import com.android.jack.dx.util.Hex; * Indirect reference to an {@link AnnotationSetItem}. */ public final class AnnotationSetRefItem extends OffsettedItem { - /** the required alignment for instances of this class */ - private static final int ALIGNMENT = 4; + /** the required alignment for instances of this class */ + private static final int ALIGNMENT = 4; - /** write size of this class, in bytes */ - private static final int WRITE_SIZE = 4; + /** write size of this class, in bytes */ + private static final int WRITE_SIZE = 4; - /** {@code non-null;} the annotation set to refer to */ - private AnnotationSetItem annotations; + /** {@code non-null;} the annotation set to refer to */ + private AnnotationSetItem annotations; - /** - * Constructs an instance. - * - * @param annotations {@code non-null;} the annotation set to refer to - */ - public AnnotationSetRefItem(AnnotationSetItem annotations) { - super(ALIGNMENT, WRITE_SIZE); + /** + * Constructs an instance. + * + * @param annotations {@code non-null;} the annotation set to refer to + */ + public AnnotationSetRefItem(AnnotationSetItem annotations) { + super(ALIGNMENT, WRITE_SIZE); - if (annotations == null) { - throw new NullPointerException("annotations == null"); - } - - this.annotations = annotations; + if (annotations == null) { + throw new NullPointerException("annotations == null"); } - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_ANNOTATION_SET_REF_ITEM; - } + this.annotations = annotations; + } - /** {@inheritDoc} */ - public void addContents(DexFile file) { - MixedItemSection wordData = file.getWordData(); + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_ANNOTATION_SET_REF_ITEM; + } - annotations = wordData.intern(annotations); - } + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + MixedItemSection wordData = file.getWordData(); - /** {@inheritDoc} */ - @Override - public String toHuman() { - return annotations.toHuman(); - } + annotations = wordData.intern(annotations); + } - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - int annotationsOff = annotations.getAbsoluteOffset(); + /** {@inheritDoc} */ + @Override + public String toHuman() { + return annotations.toHuman(); + } - if (out.annotates()) { - out.annotate(4, " annotations_off: " + Hex.u4(annotationsOff)); - } + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + int annotationsOff = annotations.getAbsoluteOffset(); - out.writeInt(annotationsOff); + if (out.annotates()) { + out.annotate(4, " annotations_off: " + Hex.u4(annotationsOff)); } + + out.writeInt(annotationsOff); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/AnnotationUtils.java b/dx/src/com/android/jack/dx/dex/file/AnnotationUtils.java index 868f82b..99bd2b9 100644 --- a/dx/src/com/android/jack/dx/dex/file/AnnotationUtils.java +++ b/dx/src/com/android/jack/dx/dex/file/AnnotationUtils.java @@ -16,6 +16,8 @@ package com.android.jack.dx.dex.file; +import static com.android.jack.dx.rop.annotation.AnnotationVisibility.SYSTEM; + import com.android.jack.dx.rop.annotation.Annotation; import com.android.jack.dx.rop.annotation.NameValuePair; import com.android.jack.dx.rop.cst.Constant; @@ -31,222 +33,219 @@ import com.android.jack.dx.rop.type.TypeList; import java.util.ArrayList; -import static com.android.jack.dx.rop.annotation.AnnotationVisibility.*; - /** * Utility class for dealing with annotations. */ public final class AnnotationUtils { - /** {@code non-null;} type for {@code AnnotationDefault} annotations */ - private static final CstType ANNOTATION_DEFAULT_TYPE = - CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;")); - - /** {@code non-null;} type for {@code EnclosingClass} annotations */ - private static final CstType ENCLOSING_CLASS_TYPE = - CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;")); - - /** {@code non-null;} type for {@code EnclosingMethod} annotations */ - private static final CstType ENCLOSING_METHOD_TYPE = - CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;")); - - /** {@code non-null;} type for {@code InnerClass} annotations */ - private static final CstType INNER_CLASS_TYPE = - CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;")); - - /** {@code non-null;} type for {@code MemberClasses} annotations */ - private static final CstType MEMBER_CLASSES_TYPE = - CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;")); - - /** {@code non-null;} type for {@code Signature} annotations */ - private static final CstType SIGNATURE_TYPE = - CstType.intern(Type.intern("Ldalvik/annotation/Signature;")); - - /** {@code non-null;} type for {@code Throws} annotations */ - private static final CstType THROWS_TYPE = - CstType.intern(Type.intern("Ldalvik/annotation/Throws;")); - - /** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */ - private static final CstString ACCESS_FLAGS_STRING = new CstString("accessFlags"); - - /** {@code non-null;} the UTF-8 constant {@code "name"} */ - private static final CstString NAME_STRING = new CstString("name"); - - /** {@code non-null;} the UTF-8 constant {@code "value"} */ - private static final CstString VALUE_STRING = new CstString("value"); - - /** - * This class is uninstantiable. - */ - private AnnotationUtils() { - // This space intentionally left blank. - } - - /** - * Constructs a standard {@code AnnotationDefault} annotation. - * - * @param defaults {@code non-null;} the defaults, itself as an annotation - * @return {@code non-null;} the constructed annotation - */ - public static Annotation makeAnnotationDefault(Annotation defaults) { - Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM); - - result.put(new NameValuePair(VALUE_STRING, new CstAnnotation(defaults))); - result.setImmutable(); - return result; - } - - /** - * Constructs a standard {@code EnclosingClass} annotation. - * - * @param clazz {@code non-null;} the enclosing class - * @return {@code non-null;} the annotation - */ - public static Annotation makeEnclosingClass(CstType clazz) { - Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM); - - result.put(new NameValuePair(VALUE_STRING, clazz)); - result.setImmutable(); - return result; - } - - /** - * Constructs a standard {@code EnclosingMethod} annotation. - * - * @param method {@code non-null;} the enclosing method - * @return {@code non-null;} the annotation + /** {@code non-null;} type for {@code AnnotationDefault} annotations */ + private static final CstType ANNOTATION_DEFAULT_TYPE = + CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;")); + + /** {@code non-null;} type for {@code EnclosingClass} annotations */ + private static final CstType ENCLOSING_CLASS_TYPE = + CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;")); + + /** {@code non-null;} type for {@code EnclosingMethod} annotations */ + private static final CstType ENCLOSING_METHOD_TYPE = + CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;")); + + /** {@code non-null;} type for {@code InnerClass} annotations */ + private static final CstType INNER_CLASS_TYPE = + CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;")); + + /** {@code non-null;} type for {@code MemberClasses} annotations */ + private static final CstType MEMBER_CLASSES_TYPE = + CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;")); + + /** {@code non-null;} type for {@code Signature} annotations */ + private static final CstType SIGNATURE_TYPE = + CstType.intern(Type.intern("Ldalvik/annotation/Signature;")); + + /** {@code non-null;} type for {@code Throws} annotations */ + private static final CstType THROWS_TYPE = + CstType.intern(Type.intern("Ldalvik/annotation/Throws;")); + + /** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */ + private static final CstString ACCESS_FLAGS_STRING = new CstString("accessFlags"); + + /** {@code non-null;} the UTF-8 constant {@code "name"} */ + private static final CstString NAME_STRING = new CstString("name"); + + /** {@code non-null;} the UTF-8 constant {@code "value"} */ + private static final CstString VALUE_STRING = new CstString("value"); + + /** + * This class is uninstantiable. + */ + private AnnotationUtils() { + // This space intentionally left blank. + } + + /** + * Constructs a standard {@code AnnotationDefault} annotation. + * + * @param defaults {@code non-null;} the defaults, itself as an annotation + * @return {@code non-null;} the constructed annotation + */ + public static Annotation makeAnnotationDefault(Annotation defaults) { + Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM); + + result.put(new NameValuePair(VALUE_STRING, new CstAnnotation(defaults))); + result.setImmutable(); + return result; + } + + /** + * Constructs a standard {@code EnclosingClass} annotation. + * + * @param clazz {@code non-null;} the enclosing class + * @return {@code non-null;} the annotation + */ + public static Annotation makeEnclosingClass(CstType clazz) { + Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM); + + result.put(new NameValuePair(VALUE_STRING, clazz)); + result.setImmutable(); + return result; + } + + /** + * Constructs a standard {@code EnclosingMethod} annotation. + * + * @param method {@code non-null;} the enclosing method + * @return {@code non-null;} the annotation + */ + public static Annotation makeEnclosingMethod(CstMethodRef method) { + Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM); + + result.put(new NameValuePair(VALUE_STRING, method)); + result.setImmutable(); + return result; + } + + /** + * Constructs a standard {@code InnerClass} annotation. + * + * @param name {@code null-ok;} the original name of the class, or + * {@code null} to represent an anonymous class + * @param accessFlags the original access flags + * @return {@code non-null;} the annotation + */ + public static Annotation makeInnerClass(CstString name, int accessFlags) { + Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM); + Constant nameCst = (name != null) ? name : CstKnownNull.THE_ONE; + + result.put(new NameValuePair(NAME_STRING, nameCst)); + result.put(new NameValuePair(ACCESS_FLAGS_STRING, CstInteger.make(accessFlags))); + result.setImmutable(); + return result; + } + + /** + * Constructs a standard {@code MemberClasses} annotation. + * + * @param types {@code non-null;} the list of (the types of) the member classes + * @return {@code non-null;} the annotation + */ + public static Annotation makeMemberClasses(TypeList types) { + CstArray array = makeCstArray(types); + Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM); + result.put(new NameValuePair(VALUE_STRING, array)); + result.setImmutable(); + return result; + } + + /** + * Constructs a standard {@code Signature} annotation. + * + * @param signature {@code non-null;} the signature string + * @return {@code non-null;} the annotation + */ + public static Annotation makeSignature(CstString signature) { + Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM); + + /* + * Split the string into pieces that are likely to be common + * across many signatures and the rest of the file. */ - public static Annotation makeEnclosingMethod(CstMethodRef method) { - Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM); - - result.put(new NameValuePair(VALUE_STRING, method)); - result.setImmutable(); - return result; - } - /** - * Constructs a standard {@code InnerClass} annotation. - * - * @param name {@code null-ok;} the original name of the class, or - * {@code null} to represent an anonymous class - * @param accessFlags the original access flags - * @return {@code non-null;} the annotation - */ - public static Annotation makeInnerClass(CstString name, int accessFlags) { - Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM); - Constant nameCst = (name != null) ? name : CstKnownNull.THE_ONE; - - result.put(new NameValuePair(NAME_STRING, nameCst)); - result.put(new NameValuePair(ACCESS_FLAGS_STRING, - CstInteger.make(accessFlags))); - result.setImmutable(); - return result; - } - - /** - * Constructs a standard {@code MemberClasses} annotation. - * - * @param types {@code non-null;} the list of (the types of) the member classes - * @return {@code non-null;} the annotation - */ - public static Annotation makeMemberClasses(TypeList types) { - CstArray array = makeCstArray(types); - Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM); - result.put(new NameValuePair(VALUE_STRING, array)); - result.setImmutable(); - return result; - } - - /** - * Constructs a standard {@code Signature} annotation. - * - * @param signature {@code non-null;} the signature string - * @return {@code non-null;} the annotation - */ - public static Annotation makeSignature(CstString signature) { - Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM); - - /* - * Split the string into pieces that are likely to be common - * across many signatures and the rest of the file. - */ - - String raw = signature.getString(); - int rawLength = raw.length(); - ArrayList<String> pieces = new ArrayList<String>(20); - - for (int at = 0; at < rawLength; /*at*/) { - char c = raw.charAt(at); - int endAt = at + 1; - if (c == 'L') { - // Scan to ';' or '<'. Consume ';' but not '<'. - while (endAt < rawLength) { - c = raw.charAt(endAt); - if (c == ';') { - endAt++; - break; - } else if (c == '<') { - break; - } - endAt++; - } - } else { - // Scan to 'L' without consuming it. - while (endAt < rawLength) { - c = raw.charAt(endAt); - if (c == 'L') { - break; - } - endAt++; - } - } - - pieces.add(raw.substring(at, endAt)); - at = endAt; +String raw = signature.getString(); + int rawLength = raw.length(); + ArrayList<String> pieces = new ArrayList<String>(20); + + for (int at = 0; at < rawLength; /*at*/) { + char c = raw.charAt(at); + int endAt = at + 1; + if (c == 'L') { + // Scan to ';' or '<'. Consume ';' but not '<'. + while (endAt < rawLength) { + c = raw.charAt(endAt); + if (c == ';') { + endAt++; + break; + } else if (c == '<') { + break; + } + endAt++; } - - int size = pieces.size(); - CstArray.List list = new CstArray.List(size); - - for (int i = 0; i < size; i++) { - list.set(i, new CstString(pieces.get(i))); + } else { + // Scan to 'L' without consuming it. + while (endAt < rawLength) { + c = raw.charAt(endAt); + if (c == 'L') { + break; + } + endAt++; } + } - list.setImmutable(); - - result.put(new NameValuePair(VALUE_STRING, new CstArray(list))); - result.setImmutable(); - return result; - } - - /** - * Constructs a standard {@code Throws} annotation. - * - * @param types {@code non-null;} the list of thrown types - * @return {@code non-null;} the annotation - */ - public static Annotation makeThrows(TypeList types) { - CstArray array = makeCstArray(types); - Annotation result = new Annotation(THROWS_TYPE, SYSTEM); - result.put(new NameValuePair(VALUE_STRING, array)); - result.setImmutable(); - return result; + pieces.add(raw.substring(at, endAt)); + at = endAt; } - /** - * Converts a {@link TypeList} to a {@link CstArray}. - * - * @param types {@code non-null;} the type list - * @return {@code non-null;} the corresponding array constant - */ - private static CstArray makeCstArray(TypeList types) { - int size = types.size(); - CstArray.List list = new CstArray.List(size); + int size = pieces.size(); + CstArray.List list = new CstArray.List(size); - for (int i = 0; i < size; i++) { - list.set(i, CstType.intern(types.getType(i))); - } + for (int i = 0; i < size; i++) { + list.set(i, new CstString(pieces.get(i))); + } - list.setImmutable(); - return new CstArray(list); + list.setImmutable(); + + result.put(new NameValuePair(VALUE_STRING, new CstArray(list))); + result.setImmutable(); + return result; + } + + /** + * Constructs a standard {@code Throws} annotation. + * + * @param types {@code non-null;} the list of thrown types + * @return {@code non-null;} the annotation + */ + public static Annotation makeThrows(TypeList types) { + CstArray array = makeCstArray(types); + Annotation result = new Annotation(THROWS_TYPE, SYSTEM); + result.put(new NameValuePair(VALUE_STRING, array)); + result.setImmutable(); + return result; + } + + /** + * Converts a {@link TypeList} to a {@link CstArray}. + * + * @param types {@code non-null;} the type list + * @return {@code non-null;} the corresponding array constant + */ + private static CstArray makeCstArray(TypeList types) { + int size = types.size(); + CstArray.List list = new CstArray.List(size); + + for (int i = 0; i < size; i++) { + list.set(i, CstType.intern(types.getType(i))); } + + list.setImmutable(); + return new CstArray(list); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/AnnotationsDirectoryItem.java b/dx/src/com/android/jack/dx/dex/file/AnnotationsDirectoryItem.java index acde7fa..13f727a 100644 --- a/dx/src/com/android/jack/dx/dex/file/AnnotationsDirectoryItem.java +++ b/dx/src/com/android/jack/dx/dex/file/AnnotationsDirectoryItem.java @@ -31,355 +31,342 @@ import java.util.Collections; * Per-class directory of annotations. */ public final class AnnotationsDirectoryItem extends OffsettedItem { - /** the required alignment for instances of this class */ - private static final int ALIGNMENT = 4; - - /** write size of this class's header, in bytes */ - private static final int HEADER_SIZE = 16; + /** the required alignment for instances of this class */ + private static final int ALIGNMENT = 4; + + /** write size of this class's header, in bytes */ + private static final int HEADER_SIZE = 16; + + /** write size of a list element, in bytes */ + private static final int ELEMENT_SIZE = 8; + + /** {@code null-ok;} the class-level annotations, if any */ + private AnnotationSetItem classAnnotations; + + /** {@code null-ok;} the annotated fields, if any */ + private ArrayList<FieldAnnotationStruct> fieldAnnotations; + + /** {@code null-ok;} the annotated methods, if any */ + private ArrayList<MethodAnnotationStruct> methodAnnotations; + + /** {@code null-ok;} the annotated parameters, if any */ + private ArrayList<ParameterAnnotationStruct> parameterAnnotations; + + /** + * Constructs an empty instance. + */ + public AnnotationsDirectoryItem() { + super(ALIGNMENT, -1); + + classAnnotations = null; + fieldAnnotations = null; + methodAnnotations = null; + parameterAnnotations = null; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM; + } + + /** + * Returns whether this item is empty (has no contents). + * + * @return {@code true} if this item is empty, or {@code false} + * if not + */ + public boolean isEmpty() { + return (classAnnotations == null) && (fieldAnnotations == null) && (methodAnnotations == null) + && (parameterAnnotations == null); + } + + /** + * Returns whether this item is a candidate for interning. The only + * interning candidates are ones that <i>only</i> have a non-null + * set of class annotations, with no other lists. + * + * @return {@code true} if this is an interning candidate, or + * {@code false} if not + */ + public boolean isInternable() { + return (classAnnotations != null) && (fieldAnnotations == null) && (methodAnnotations == null) + && (parameterAnnotations == null); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + if (classAnnotations == null) { + return 0; + } - /** write size of a list element, in bytes */ - private static final int ELEMENT_SIZE = 8; + return classAnnotations.hashCode(); + } + + /** + * {@inheritDoc} + * + * <p><b>Note:</b>: This throws an exception if this item is not + * internable.</p> + * + * @see #isInternable + */ + @Override + public int compareTo0(OffsettedItem other) { + if (!isInternable()) { + throw new UnsupportedOperationException("uninternable instance"); + } - /** {@code null-ok;} the class-level annotations, if any */ - private AnnotationSetItem classAnnotations; + AnnotationsDirectoryItem otherDirectory = (AnnotationsDirectoryItem) other; + return classAnnotations.compareTo(otherDirectory.classAnnotations); + } + + /** + * Sets the direct annotations on this instance. These are annotations + * made on the class, per se, as opposed to on one of its members. + * It is only valid to call this method at most once per instance. + * + * @param annotations {@code non-null;} annotations to set for this class + */ + public void setClassAnnotations(Annotations annotations) { + if (annotations == null) { + throw new NullPointerException("annotations == null"); + } - /** {@code null-ok;} the annotated fields, if any */ - private ArrayList<FieldAnnotationStruct> fieldAnnotations; + if (classAnnotations != null) { + throw new UnsupportedOperationException("class annotations already set"); + } - /** {@code null-ok;} the annotated methods, if any */ - private ArrayList<MethodAnnotationStruct> methodAnnotations; + classAnnotations = new AnnotationSetItem(annotations); + } + + /** + * Adds a field annotations item to this instance. + * + * @param field {@code non-null;} field in question + * @param annotations {@code non-null;} associated annotations to add + */ + public void addFieldAnnotations(CstFieldRef field, Annotations annotations) { + if (fieldAnnotations == null) { + fieldAnnotations = new ArrayList<FieldAnnotationStruct>(); + } - /** {@code null-ok;} the annotated parameters, if any */ - private ArrayList<ParameterAnnotationStruct> parameterAnnotations; + fieldAnnotations.add(new FieldAnnotationStruct(field, new AnnotationSetItem(annotations))); + } + + /** + * Adds a method annotations item to this instance. + * + * @param method {@code non-null;} method in question + * @param annotations {@code non-null;} associated annotations to add + */ + public void addMethodAnnotations(CstMethodRef method, Annotations annotations) { + if (methodAnnotations == null) { + methodAnnotations = new ArrayList<MethodAnnotationStruct>(); + } - /** - * Constructs an empty instance. - */ - public AnnotationsDirectoryItem() { - super(ALIGNMENT, -1); + methodAnnotations.add(new MethodAnnotationStruct(method, new AnnotationSetItem(annotations))); + } + + /** + * Adds a parameter annotations item to this instance. + * + * @param method {@code non-null;} method in question + * @param list {@code non-null;} associated list of annotation sets to add + */ + public void addParameterAnnotations(CstMethodRef method, AnnotationsList list) { + if (parameterAnnotations == null) { + parameterAnnotations = new ArrayList<ParameterAnnotationStruct>(); + } - classAnnotations = null; - fieldAnnotations = null; - methodAnnotations = null; - parameterAnnotations = null; + parameterAnnotations.add(new ParameterAnnotationStruct(method, list)); + } + + /** + * Gets the method annotations for a given method, if any. This is + * meant for use by debugging / dumping code. + * + * @param method {@code non-null;} the method + * @return {@code null-ok;} the method annotations, if any + */ + public Annotations getMethodAnnotations(CstMethodRef method) { + if (methodAnnotations == null) { + return null; } - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM; + for (MethodAnnotationStruct item : methodAnnotations) { + if (item.getMethod().equals(method)) { + return item.getAnnotations(); + } } - /** - * Returns whether this item is empty (has no contents). - * - * @return {@code true} if this item is empty, or {@code false} - * if not - */ - public boolean isEmpty() { - return (classAnnotations == null) && - (fieldAnnotations == null) && - (methodAnnotations == null) && - (parameterAnnotations == null); + return null; + } + + /** + * Gets the parameter annotations for a given method, if any. This is + * meant for use by debugging / dumping code. + * + * @param method {@code non-null;} the method + * @return {@code null-ok;} the parameter annotations, if any + */ + public AnnotationsList getParameterAnnotations(CstMethodRef method) { + if (parameterAnnotations == null) { + return null; } - /** - * Returns whether this item is a candidate for interning. The only - * interning candidates are ones that <i>only</i> have a non-null - * set of class annotations, with no other lists. - * - * @return {@code true} if this is an interning candidate, or - * {@code false} if not - */ - public boolean isInternable() { - return (classAnnotations != null) && - (fieldAnnotations == null) && - (methodAnnotations == null) && - (parameterAnnotations == null); + for (ParameterAnnotationStruct item : parameterAnnotations) { + if (item.getMethod().equals(method)) { + return item.getAnnotationsList(); + } } - /** {@inheritDoc} */ - @Override - public int hashCode() { - if (classAnnotations == null) { - return 0; - } + return null; + } - return classAnnotations.hashCode(); - } + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + MixedItemSection wordData = file.getWordData(); - /** - * {@inheritDoc} - * - * <p><b>Note:</b>: This throws an exception if this item is not - * internable.</p> - * - * @see #isInternable - */ - @Override - public int compareTo0(OffsettedItem other) { - if (! isInternable()) { - throw new UnsupportedOperationException("uninternable instance"); - } - - AnnotationsDirectoryItem otherDirectory = - (AnnotationsDirectoryItem) other; - return classAnnotations.compareTo(otherDirectory.classAnnotations); + if (classAnnotations != null) { + classAnnotations = wordData.intern(classAnnotations); } - /** - * Sets the direct annotations on this instance. These are annotations - * made on the class, per se, as opposed to on one of its members. - * It is only valid to call this method at most once per instance. - * - * @param annotations {@code non-null;} annotations to set for this class - */ - public void setClassAnnotations(Annotations annotations) { - if (annotations == null) { - throw new NullPointerException("annotations == null"); - } - - if (classAnnotations != null) { - throw new UnsupportedOperationException( - "class annotations already set"); - } - - classAnnotations = new AnnotationSetItem(annotations); + if (fieldAnnotations != null) { + for (FieldAnnotationStruct item : fieldAnnotations) { + item.addContents(file); + } } - /** - * Adds a field annotations item to this instance. - * - * @param field {@code non-null;} field in question - * @param annotations {@code non-null;} associated annotations to add - */ - public void addFieldAnnotations(CstFieldRef field, - Annotations annotations) { - if (fieldAnnotations == null) { - fieldAnnotations = new ArrayList<FieldAnnotationStruct>(); - } - - fieldAnnotations.add(new FieldAnnotationStruct(field, - new AnnotationSetItem(annotations))); + if (methodAnnotations != null) { + for (MethodAnnotationStruct item : methodAnnotations) { + item.addContents(file); + } } - /** - * Adds a method annotations item to this instance. - * - * @param method {@code non-null;} method in question - * @param annotations {@code non-null;} associated annotations to add - */ - public void addMethodAnnotations(CstMethodRef method, - Annotations annotations) { - if (methodAnnotations == null) { - methodAnnotations = new ArrayList<MethodAnnotationStruct>(); - } - - methodAnnotations.add(new MethodAnnotationStruct(method, - new AnnotationSetItem(annotations))); + if (parameterAnnotations != null) { + for (ParameterAnnotationStruct item : parameterAnnotations) { + item.addContents(file); + } } - - /** - * Adds a parameter annotations item to this instance. - * - * @param method {@code non-null;} method in question - * @param list {@code non-null;} associated list of annotation sets to add - */ - public void addParameterAnnotations(CstMethodRef method, - AnnotationsList list) { - if (parameterAnnotations == null) { - parameterAnnotations = new ArrayList<ParameterAnnotationStruct>(); - } - - parameterAnnotations.add(new ParameterAnnotationStruct(method, list)); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + throw new RuntimeException("unsupported"); + } + + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + // We just need to set the write size here. + + int elementCount = + listSize(fieldAnnotations) + listSize(methodAnnotations) + listSize(parameterAnnotations); + setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE)); + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations); + int fieldsSize = listSize(fieldAnnotations); + int methodsSize = listSize(methodAnnotations); + int parametersSize = listSize(parameterAnnotations); + + if (annotates) { + out.annotate(0, offsetString() + " annotations directory"); + out.annotate(4, " class_annotations_off: " + Hex.u4(classOff)); + out.annotate(4, " fields_size: " + Hex.u4(fieldsSize)); + out.annotate(4, " methods_size: " + Hex.u4(methodsSize)); + out.annotate(4, " parameters_size: " + Hex.u4(parametersSize)); } - /** - * Gets the method annotations for a given method, if any. This is - * meant for use by debugging / dumping code. - * - * @param method {@code non-null;} the method - * @return {@code null-ok;} the method annotations, if any - */ - public Annotations getMethodAnnotations(CstMethodRef method) { - if (methodAnnotations == null) { - return null; - } - - for (MethodAnnotationStruct item : methodAnnotations) { - if (item.getMethod().equals(method)) { - return item.getAnnotations(); - } - } - - return null; + out.writeInt(classOff); + out.writeInt(fieldsSize); + out.writeInt(methodsSize); + out.writeInt(parametersSize); + + if (fieldsSize != 0) { + Collections.sort(fieldAnnotations); + if (annotates) { + out.annotate(0, " fields:"); + } + for (FieldAnnotationStruct item : fieldAnnotations) { + item.writeTo(file, out); + } } - /** - * Gets the parameter annotations for a given method, if any. This is - * meant for use by debugging / dumping code. - * - * @param method {@code non-null;} the method - * @return {@code null-ok;} the parameter annotations, if any - */ - public AnnotationsList getParameterAnnotations(CstMethodRef method) { - if (parameterAnnotations == null) { - return null; - } - - for (ParameterAnnotationStruct item : parameterAnnotations) { - if (item.getMethod().equals(method)) { - return item.getAnnotationsList(); - } - } - - return null; + if (methodsSize != 0) { + Collections.sort(methodAnnotations); + if (annotates) { + out.annotate(0, " methods:"); + } + for (MethodAnnotationStruct item : methodAnnotations) { + item.writeTo(file, out); + } } - /** {@inheritDoc} */ - public void addContents(DexFile file) { - MixedItemSection wordData = file.getWordData(); - - if (classAnnotations != null) { - classAnnotations = wordData.intern(classAnnotations); - } - - if (fieldAnnotations != null) { - for (FieldAnnotationStruct item : fieldAnnotations) { - item.addContents(file); - } - } - - if (methodAnnotations != null) { - for (MethodAnnotationStruct item : methodAnnotations) { - item.addContents(file); - } - } - - if (parameterAnnotations != null) { - for (ParameterAnnotationStruct item : parameterAnnotations) { - item.addContents(file); - } - } + if (parametersSize != 0) { + Collections.sort(parameterAnnotations); + if (annotates) { + out.annotate(0, " parameters:"); + } + for (ParameterAnnotationStruct item : parameterAnnotations) { + item.writeTo(file, out); + } } - - /** {@inheritDoc} */ - @Override - public String toHuman() { - throw new RuntimeException("unsupported"); + } + + /** + * Gets the list size of the given list, or {@code 0} if given + * {@code null}. + * + * @param list {@code null-ok;} the list in question + * @return {@code >= 0;} its size + */ + private static int listSize(ArrayList<?> list) { + if (list == null) { + return 0; } - /** {@inheritDoc} */ - @Override - protected void place0(Section addedTo, int offset) { - // We just need to set the write size here. - - int elementCount = listSize(fieldAnnotations) - + listSize(methodAnnotations) + listSize(parameterAnnotations); - setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE)); + return list.size(); + } + + /** + * Prints out the contents of this instance, in a debugging-friendly + * way. This is meant to be called from {@link ClassDefItem#debugPrint}. + * + * @param out {@code non-null;} where to output to + */ + /*package*/void debugPrint(PrintWriter out) { + if (classAnnotations != null) { + out.println(" class annotations: " + classAnnotations); } - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - boolean annotates = out.annotates(); - int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations); - int fieldsSize = listSize(fieldAnnotations); - int methodsSize = listSize(methodAnnotations); - int parametersSize = listSize(parameterAnnotations); - - if (annotates) { - out.annotate(0, offsetString() + " annotations directory"); - out.annotate(4, " class_annotations_off: " + Hex.u4(classOff)); - out.annotate(4, " fields_size: " + - Hex.u4(fieldsSize)); - out.annotate(4, " methods_size: " + - Hex.u4(methodsSize)); - out.annotate(4, " parameters_size: " + - Hex.u4(parametersSize)); - } - - out.writeInt(classOff); - out.writeInt(fieldsSize); - out.writeInt(methodsSize); - out.writeInt(parametersSize); - - if (fieldsSize != 0) { - Collections.sort(fieldAnnotations); - if (annotates) { - out.annotate(0, " fields:"); - } - for (FieldAnnotationStruct item : fieldAnnotations) { - item.writeTo(file, out); - } - } - - if (methodsSize != 0) { - Collections.sort(methodAnnotations); - if (annotates) { - out.annotate(0, " methods:"); - } - for (MethodAnnotationStruct item : methodAnnotations) { - item.writeTo(file, out); - } - } - - if (parametersSize != 0) { - Collections.sort(parameterAnnotations); - if (annotates) { - out.annotate(0, " parameters:"); - } - for (ParameterAnnotationStruct item : parameterAnnotations) { - item.writeTo(file, out); - } - } + if (fieldAnnotations != null) { + out.println(" field annotations:"); + for (FieldAnnotationStruct item : fieldAnnotations) { + out.println(" " + item.toHuman()); + } } - /** - * Gets the list size of the given list, or {@code 0} if given - * {@code null}. - * - * @param list {@code null-ok;} the list in question - * @return {@code >= 0;} its size - */ - private static int listSize(ArrayList<?> list) { - if (list == null) { - return 0; - } - - return list.size(); + if (methodAnnotations != null) { + out.println(" method annotations:"); + for (MethodAnnotationStruct item : methodAnnotations) { + out.println(" " + item.toHuman()); + } } - /** - * Prints out the contents of this instance, in a debugging-friendly - * way. This is meant to be called from {@link ClassDefItem#debugPrint}. - * - * @param out {@code non-null;} where to output to - */ - /*package*/ void debugPrint(PrintWriter out) { - if (classAnnotations != null) { - out.println(" class annotations: " + classAnnotations); - } - - if (fieldAnnotations != null) { - out.println(" field annotations:"); - for (FieldAnnotationStruct item : fieldAnnotations) { - out.println(" " + item.toHuman()); - } - } - - if (methodAnnotations != null) { - out.println(" method annotations:"); - for (MethodAnnotationStruct item : methodAnnotations) { - out.println(" " + item.toHuman()); - } - } - - if (parameterAnnotations != null) { - out.println(" parameter annotations:"); - for (ParameterAnnotationStruct item : parameterAnnotations) { - out.println(" " + item.toHuman()); - } - } + if (parameterAnnotations != null) { + out.println(" parameter annotations:"); + for (ParameterAnnotationStruct item : parameterAnnotations) { + out.println(" " + item.toHuman()); + } } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/CatchStructs.java b/dx/src/com/android/jack/dx/dex/file/CatchStructs.java index 686ef82..3426fac 100644 --- a/dx/src/com/android/jack/dx/dex/file/CatchStructs.java +++ b/dx/src/com/android/jack/dx/dex/file/CatchStructs.java @@ -34,282 +34,284 @@ import java.util.TreeMap; * {@code catch_handler_item[]}. */ public final class CatchStructs { - /** - * the size of a {@code try_item}: a {@code uint} - * and two {@code ushort}s - */ - public static final int TRY_ITEM_WRITE_SIZE = 4 + (2 * 2); - - /** {@code non-null;} code that contains the catches */ - private final DalvCode code; - - /** - * {@code null-ok;} the underlying table; set in - * {@link #finishProcessingIfNecessary} - */ - private CatchTable table; - - /** - * {@code null-ok;} the encoded handler list, if calculated; set in - * {@link #encode} - */ - private byte[] encodedHandlers; - - /** - * length of the handlers header (encoded size), if known; used for - * annotation - */ - private int encodedHandlerHeaderSize; - - /** - * {@code null-ok;} map from handler lists to byte offsets, if calculated; set in - * {@link #encode} - */ - private TreeMap<CatchHandlerList, Integer> handlerOffsets; - - /** - * Constructs an instance. - * - * @param code {@code non-null;} code that contains the catches + /** + * the size of a {@code try_item}: a {@code uint} + * and two {@code ushort}s + */ + public static final int TRY_ITEM_WRITE_SIZE = 4 + (2 * 2); + + /** {@code non-null;} code that contains the catches */ + private final DalvCode code; + + /** + * {@code null-ok;} the underlying table; set in + * {@link #finishProcessingIfNecessary} + */ + private CatchTable table; + + /** + * {@code null-ok;} the encoded handler list, if calculated; set in + * {@link #encode} + */ + private byte[] encodedHandlers; + + /** + * length of the handlers header (encoded size), if known; used for + * annotation + */ + private int encodedHandlerHeaderSize; + + /** + * {@code null-ok;} map from handler lists to byte offsets, if calculated; set in + * {@link #encode} + */ + private TreeMap<CatchHandlerList, Integer> handlerOffsets; + + /** + * Constructs an instance. + * + * @param code {@code non-null;} code that contains the catches + */ + public CatchStructs(DalvCode code) { + this.code = code; + this.table = null; + this.encodedHandlers = null; + this.encodedHandlerHeaderSize = 0; + this.handlerOffsets = null; + } + + /** + * Finish processing the catches, if necessary. + */ + private void finishProcessingIfNecessary() { + if (table == null) { + table = code.getCatches(); + } + } + + /** + * Gets the size of the tries list, in entries. + * + * @return {@code >= 0;} the tries list size + */ + public int triesSize() { + finishProcessingIfNecessary(); + return table.size(); + } + + /** + * Does a human-friendly dump of this instance. + * + * @param out {@code non-null;} where to dump + * @param prefix {@code non-null;} prefix to attach to each line of output + */ + public void debugPrint(PrintWriter out, String prefix) { + annotateEntries(prefix, out, null); + } + + /** + * Encodes the handler lists. + * + * @param file {@code non-null;} file this instance is part of + */ + public void encode(DexFile file) { + finishProcessingIfNecessary(); + + TypeIdsSection typeIds = file.getTypeIds(); + int size = table.size(); + + handlerOffsets = new TreeMap<CatchHandlerList, Integer>(); + + /* + * First add a map entry for each unique list. The tree structure + * will ensure they are sorted when we reiterate later. */ - public CatchStructs(DalvCode code) { - this.code = code; - this.table = null; - this.encodedHandlers = null; - this.encodedHandlerHeaderSize = 0; - this.handlerOffsets = null; + for (int i = 0; i < size; i++) { + handlerOffsets.put(table.get(i).getHandlers(), null); } - /** - * Finish processing the catches, if necessary. - */ - private void finishProcessingIfNecessary() { - if (table == null) { - table = code.getCatches(); - } + if (handlerOffsets.size() > 65535) { + throw new UnsupportedOperationException("too many catch handlers"); } - /** - * Gets the size of the tries list, in entries. - * - * @return {@code >= 0;} the tries list size - */ - public int triesSize() { - finishProcessingIfNecessary(); - return table.size(); + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + + // Write out the handlers "header" consisting of its size in entries. + encodedHandlerHeaderSize = out.writeUleb128(handlerOffsets.size()); + + // Now write the lists out in order, noting the offset of each. + for (Map.Entry<CatchHandlerList, Integer> mapping : handlerOffsets.entrySet()) { + CatchHandlerList list = mapping.getKey(); + int listSize = list.size(); + boolean catchesAll = list.catchesAll(); + + // Set the offset before we do any writing. + mapping.setValue(out.getCursor()); + + if (catchesAll) { + // A size <= 0 means that the list ends with a catch-all. + out.writeSleb128(-(listSize - 1)); + listSize--; + } else { + out.writeSleb128(listSize); + } + + for (int i = 0; i < listSize; i++) { + CatchHandlerList.Entry entry = list.get(i); + out.writeUleb128(typeIds.indexOf(entry.getExceptionType())); + out.writeUleb128(entry.getHandler()); + } + + if (catchesAll) { + out.writeUleb128(list.get(listSize).getHandler()); + } } - /** - * Does a human-friendly dump of this instance. - * - * @param out {@code non-null;} where to dump - * @param prefix {@code non-null;} prefix to attach to each line of output - */ - public void debugPrint(PrintWriter out, String prefix) { - annotateEntries(prefix, out, null); + encodedHandlers = out.toByteArray(); + } + + /** + * Gets the write size of this instance, in bytes. + * + * @return {@code >= 0;} the write size + */ + public int writeSize() { + return (triesSize() * TRY_ITEM_WRITE_SIZE) + encodedHandlers.length; + } + + /** + * Writes this instance to the given stream. + * + * @param file {@code non-null;} file this instance is part of + * @param out {@code non-null;} where to write to + */ + public void writeTo(DexFile file, AnnotatedOutput out) { + finishProcessingIfNecessary(); + + if (out.annotates()) { + annotateEntries(" ", null, out); } - /** - * Encodes the handler lists. - * - * @param file {@code non-null;} file this instance is part of - */ - public void encode(DexFile file) { - finishProcessingIfNecessary(); - - TypeIdsSection typeIds = file.getTypeIds(); - int size = table.size(); - - handlerOffsets = new TreeMap<CatchHandlerList, Integer>(); - - /* - * First add a map entry for each unique list. The tree structure - * will ensure they are sorted when we reiterate later. - */ - for (int i = 0; i < size; i++) { - handlerOffsets.put(table.get(i).getHandlers(), null); - } - - if (handlerOffsets.size() > 65535) { - throw new UnsupportedOperationException( - "too many catch handlers"); - } - - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); - - // Write out the handlers "header" consisting of its size in entries. - encodedHandlerHeaderSize = - out.writeUleb128(handlerOffsets.size()); - - // Now write the lists out in order, noting the offset of each. - for (Map.Entry<CatchHandlerList, Integer> mapping : - handlerOffsets.entrySet()) { - CatchHandlerList list = mapping.getKey(); - int listSize = list.size(); - boolean catchesAll = list.catchesAll(); - - // Set the offset before we do any writing. - mapping.setValue(out.getCursor()); - - if (catchesAll) { - // A size <= 0 means that the list ends with a catch-all. - out.writeSleb128(-(listSize - 1)); - listSize--; - } else { - out.writeSleb128(listSize); - } - - for (int i = 0; i < listSize; i++) { - CatchHandlerList.Entry entry = list.get(i); - out.writeUleb128( - typeIds.indexOf(entry.getExceptionType())); - out.writeUleb128(entry.getHandler()); - } - - if (catchesAll) { - out.writeUleb128(list.get(listSize).getHandler()); - } - } - - encodedHandlers = out.toByteArray(); + int tableSize = table.size(); + for (int i = 0; i < tableSize; i++) { + CatchTable.Entry one = table.get(i); + int start = one.getStart(); + int end = one.getEnd(); + int insnCount = end - start; + + if (insnCount >= 65536) { + throw new UnsupportedOperationException( + "bogus exception range: " + Hex.u4(start) + ".." + Hex.u4(end)); + } + + out.writeInt(start); + out.writeShort(insnCount); + out.writeShort(handlerOffsets.get(one.getHandlers())); } - /** - * Gets the write size of this instance, in bytes. - * - * @return {@code >= 0;} the write size - */ - public int writeSize() { - return (triesSize() * TRY_ITEM_WRITE_SIZE) + - + encodedHandlers.length; + out.write(encodedHandlers); + } + + /** + * Helper method to annotate or simply print the exception handlers. + * Only one of {@code printTo} or {@code annotateTo} should + * be non-null. + * + * @param prefix {@code non-null;} prefix for each line + * @param printTo {@code null-ok;} where to print to + * @param annotateTo {@code null-ok;} where to consume bytes and annotate to + */ + private void annotateEntries(String prefix, PrintWriter printTo, AnnotatedOutput annotateTo) { + finishProcessingIfNecessary(); + + boolean consume = (annotateTo != null); + int amt1 = consume ? 6 : 0; + int amt2 = consume ? 2 : 0; + int size = table.size(); + String subPrefix = prefix + " "; + + if (consume) { + annotateTo.annotate(0, prefix + "tries:"); + } else { + printTo.println(prefix + "tries:"); } - /** - * Writes this instance to the given stream. - * - * @param file {@code non-null;} file this instance is part of - * @param out {@code non-null;} where to write to - */ - public void writeTo(DexFile file, AnnotatedOutput out) { - finishProcessingIfNecessary(); - - if (out.annotates()) { - annotateEntries(" ", null, out); - } - - int tableSize = table.size(); - for (int i = 0; i < tableSize; i++) { - CatchTable.Entry one = table.get(i); - int start = one.getStart(); - int end = one.getEnd(); - int insnCount = end - start; - - if (insnCount >= 65536) { - throw new UnsupportedOperationException( - "bogus exception range: " + Hex.u4(start) + ".." + - Hex.u4(end)); - } - - out.writeInt(start); - out.writeShort(insnCount); - out.writeShort(handlerOffsets.get(one.getHandlers())); - } - - out.write(encodedHandlers); + for (int i = 0; i < size; i++) { + CatchTable.Entry entry = table.get(i); + CatchHandlerList handlers = entry.getHandlers(); + String s1 = + subPrefix + "try " + Hex.u2or4(entry.getStart()) + ".." + Hex.u2or4(entry.getEnd()); + String s2 = handlers.toHuman(subPrefix, ""); + + if (consume) { + annotateTo.annotate(amt1, s1); + annotateTo.annotate(amt2, s2); + } else { + printTo.println(s1); + printTo.println(s2); + } } - /** - * Helper method to annotate or simply print the exception handlers. - * Only one of {@code printTo} or {@code annotateTo} should - * be non-null. - * - * @param prefix {@code non-null;} prefix for each line - * @param printTo {@code null-ok;} where to print to - * @param annotateTo {@code null-ok;} where to consume bytes and annotate to - */ - private void annotateEntries(String prefix, PrintWriter printTo, - AnnotatedOutput annotateTo) { - finishProcessingIfNecessary(); - - boolean consume = (annotateTo != null); - int amt1 = consume ? 6 : 0; - int amt2 = consume ? 2 : 0; - int size = table.size(); - String subPrefix = prefix + " "; - - if (consume) { - annotateTo.annotate(0, prefix + "tries:"); - } else { - printTo.println(prefix + "tries:"); - } - - for (int i = 0; i < size; i++) { - CatchTable.Entry entry = table.get(i); - CatchHandlerList handlers = entry.getHandlers(); - String s1 = subPrefix + "try " + Hex.u2or4(entry.getStart()) - + ".." + Hex.u2or4(entry.getEnd()); - String s2 = handlers.toHuman(subPrefix, ""); - - if (consume) { - annotateTo.annotate(amt1, s1); - annotateTo.annotate(amt2, s2); - } else { - printTo.println(s1); - printTo.println(s2); - } - } - - if (! consume) { - // Only emit the handler lists if we are consuming bytes. - return; - } - - annotateTo.annotate(0, prefix + "handlers:"); - annotateTo.annotate(encodedHandlerHeaderSize, - subPrefix + "size: " + Hex.u2(handlerOffsets.size())); - - int lastOffset = 0; - CatchHandlerList lastList = null; - - for (Map.Entry<CatchHandlerList, Integer> mapping : - handlerOffsets.entrySet()) { - CatchHandlerList list = mapping.getKey(); - int offset = mapping.getValue(); - - if (lastList != null) { - annotateAndConsumeHandlers(lastList, lastOffset, - offset - lastOffset, subPrefix, printTo, annotateTo); - } - - lastList = list; - lastOffset = offset; - } - - annotateAndConsumeHandlers(lastList, lastOffset, - encodedHandlers.length - lastOffset, - subPrefix, printTo, annotateTo); + if (!consume) { + // Only emit the handler lists if we are consuming bytes. + return; } - /** - * Helper for {@link #annotateEntries} to annotate a catch handler list - * while consuming it. - * - * @param handlers {@code non-null;} handlers to annotate - * @param offset {@code >= 0;} the offset of this handler - * @param size {@code >= 1;} the number of bytes the handlers consume - * @param prefix {@code non-null;} prefix for each line - * @param printTo {@code null-ok;} where to print to - * @param annotateTo {@code non-null;} where to annotate to - */ - private static void annotateAndConsumeHandlers(CatchHandlerList handlers, - int offset, int size, String prefix, PrintWriter printTo, - AnnotatedOutput annotateTo) { - String s = handlers.toHuman(prefix, Hex.u2(offset) + ": "); + annotateTo.annotate(0, prefix + "handlers:"); + annotateTo.annotate(encodedHandlerHeaderSize, + subPrefix + "size: " + Hex.u2(handlerOffsets.size())); + + int lastOffset = 0; + CatchHandlerList lastList = null; + + for (Map.Entry<CatchHandlerList, Integer> mapping : handlerOffsets.entrySet()) { + CatchHandlerList list = mapping.getKey(); + int offset = mapping.getValue(); - if (printTo != null) { - printTo.println(s); - } + if (lastList != null) { + annotateAndConsumeHandlers(lastList, + lastOffset, + offset - lastOffset, + subPrefix, + printTo, + annotateTo); + } - annotateTo.annotate(size, s); + lastList = list; + lastOffset = offset; } + + annotateAndConsumeHandlers(lastList, + lastOffset, + encodedHandlers.length - lastOffset, + subPrefix, + printTo, + annotateTo); + } + + /** + * Helper for {@link #annotateEntries} to annotate a catch handler list + * while consuming it. + * + * @param handlers {@code non-null;} handlers to annotate + * @param offset {@code >= 0;} the offset of this handler + * @param size {@code >= 1;} the number of bytes the handlers consume + * @param prefix {@code non-null;} prefix for each line + * @param printTo {@code null-ok;} where to print to + * @param annotateTo {@code non-null;} where to annotate to + */ + private static void annotateAndConsumeHandlers(CatchHandlerList handlers, + int offset, + int size, + String prefix, + PrintWriter printTo, + AnnotatedOutput annotateTo) { + String s = handlers.toHuman(prefix, Hex.u2(offset) + ": "); + + if (printTo != null) { + printTo.println(s); + } + + annotateTo.annotate(size, s); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/ClassDataItem.java b/dx/src/com/android/jack/dx/dex/file/ClassDataItem.java index 8ca353c..7e9ccce 100644 --- a/dx/src/com/android/jack/dx/dex/file/ClassDataItem.java +++ b/dx/src/com/android/jack/dx/dex/file/ClassDataItem.java @@ -38,389 +38,385 @@ import java.util.HashMap; * {@code dex} file, as opposed to a random-access form. */ public final class ClassDataItem extends OffsettedItem { - /** {@code non-null;} what class this data is for, just for listing generation */ - private final CstType thisClass; + /** {@code non-null;} what class this data is for, just for listing generation */ + private final CstType thisClass; - /** {@code non-null;} list of static fields */ - private final ArrayList<EncodedField> staticFields; + /** {@code non-null;} list of static fields */ + private final ArrayList<EncodedField> staticFields; - /** {@code non-null;} list of initial values for static fields */ - private final HashMap<EncodedField, Constant> staticValues; + /** {@code non-null;} list of initial values for static fields */ + private final HashMap<EncodedField, Constant> staticValues; - /** {@code non-null;} list of instance fields */ - private final ArrayList<EncodedField> instanceFields; + /** {@code non-null;} list of instance fields */ + private final ArrayList<EncodedField> instanceFields; - /** {@code non-null;} list of direct methods */ - private final ArrayList<EncodedMethod> directMethods; + /** {@code non-null;} list of direct methods */ + private final ArrayList<EncodedMethod> directMethods; - /** {@code non-null;} list of virtual methods */ - private final ArrayList<EncodedMethod> virtualMethods; + /** {@code non-null;} list of virtual methods */ + private final ArrayList<EncodedMethod> virtualMethods; - /** {@code null-ok;} static initializer list; set in {@link #addContents} */ - private CstArray staticValuesConstant; + /** {@code null-ok;} static initializer list; set in {@link #addContents} */ + private CstArray staticValuesConstant; - /** - * {@code null-ok;} encoded form, ready for writing to a file; set during - * {@link #place0} - */ - private byte[] encodedForm; - - /** - * Constructs an instance. Its sets of members are initially - * empty. - * - * @param thisClass {@code non-null;} what class this data is for, just - * for listing generation - */ - public ClassDataItem(CstType thisClass) { - super(1, -1); + /** + * {@code null-ok;} encoded form, ready for writing to a file; set during + * {@link #place0} + */ + private byte[] encodedForm; - if (thisClass == null) { - throw new NullPointerException("thisClass == null"); - } + /** + * Constructs an instance. Its sets of members are initially + * empty. + * + * @param thisClass {@code non-null;} what class this data is for, just + * for listing generation + */ + public ClassDataItem(CstType thisClass) { + super(1, -1); - this.thisClass = thisClass; - this.staticFields = new ArrayList<EncodedField>(20); - this.staticValues = new HashMap<EncodedField, Constant>(40); - this.instanceFields = new ArrayList<EncodedField>(20); - this.directMethods = new ArrayList<EncodedMethod>(20); - this.virtualMethods = new ArrayList<EncodedMethod>(20); - this.staticValuesConstant = null; + if (thisClass == null) { + throw new NullPointerException("thisClass == null"); } - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_CLASS_DATA_ITEM; + this.thisClass = thisClass; + this.staticFields = new ArrayList<EncodedField>(20); + this.staticValues = new HashMap<EncodedField, Constant>(40); + this.instanceFields = new ArrayList<EncodedField>(20); + this.directMethods = new ArrayList<EncodedMethod>(20); + this.virtualMethods = new ArrayList<EncodedMethod>(20); + this.staticValuesConstant = null; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_CLASS_DATA_ITEM; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return toString(); + } + + /** + * Returns whether this instance is empty. + * + * @return {@code true} if this instance is empty or + * {@code false} if at least one element has been added to it + */ + public boolean isEmpty() { + return staticFields.isEmpty() && instanceFields.isEmpty() && directMethods.isEmpty() + && virtualMethods.isEmpty(); + } + + /** + * Adds a static field. + * + * @param field {@code non-null;} the field to add + * @param value {@code null-ok;} initial value for the field, if any + */ + public void addStaticField(EncodedField field, Constant value) { + if (field == null) { + throw new NullPointerException("field == null"); } - /** {@inheritDoc} */ - @Override - public String toHuman() { - return toString(); + if (staticValuesConstant != null) { + throw new UnsupportedOperationException("static fields already sorted"); } - /** - * Returns whether this instance is empty. - * - * @return {@code true} if this instance is empty or - * {@code false} if at least one element has been added to it - */ - public boolean isEmpty() { - return staticFields.isEmpty() && instanceFields.isEmpty() - && directMethods.isEmpty() && virtualMethods.isEmpty(); + staticFields.add(field); + staticValues.put(field, value); + } + + /** + * Adds an instance field. + * + * @param field {@code non-null;} the field to add + */ + public void addInstanceField(EncodedField field) { + if (field == null) { + throw new NullPointerException("field == null"); } - /** - * Adds a static field. - * - * @param field {@code non-null;} the field to add - * @param value {@code null-ok;} initial value for the field, if any - */ - public void addStaticField(EncodedField field, Constant value) { - if (field == null) { - throw new NullPointerException("field == null"); - } - - if (staticValuesConstant != null) { - throw new UnsupportedOperationException( - "static fields already sorted"); - } - - staticFields.add(field); - staticValues.put(field, value); + instanceFields.add(field); + } + + /** + * Adds a direct ({@code static} and/or {@code private}) method. + * + * @param method {@code non-null;} the method to add + */ + public void addDirectMethod(EncodedMethod method) { + if (method == null) { + throw new NullPointerException("method == null"); } - /** - * Adds an instance field. - * - * @param field {@code non-null;} the field to add - */ - public void addInstanceField(EncodedField field) { - if (field == null) { - throw new NullPointerException("field == null"); - } - - instanceFields.add(field); + directMethods.add(method); + } + + /** + * Adds a virtual method. + * + * @param method {@code non-null;} the method to add + */ + public void addVirtualMethod(EncodedMethod method) { + if (method == null) { + throw new NullPointerException("method == null"); } - /** - * Adds a direct ({@code static} and/or {@code private}) method. - * - * @param method {@code non-null;} the method to add - */ - public void addDirectMethod(EncodedMethod method) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - directMethods.add(method); + virtualMethods.add(method); + } + + /** + * Gets all the methods in this class. The returned list is not linked + * in any way to the underlying lists contained in this instance, but + * the objects contained in the list are shared. + * + * @return {@code non-null;} list of all methods + */ + public ArrayList<EncodedMethod> getMethods() { + int sz = directMethods.size() + virtualMethods.size(); + ArrayList<EncodedMethod> result = new ArrayList<EncodedMethod>(sz); + + result.addAll(directMethods); + result.addAll(virtualMethods); + + return result; + } + + + /** + * Prints out the contents of this instance, in a debugging-friendly + * way. + * + * @param out {@code non-null;} where to output to + * @param verbose whether to be verbose with the output + */ + public void debugPrint(Writer out, boolean verbose) { + PrintWriter pw = Writers.printWriterFor(out); + + int sz = staticFields.size(); + for (int i = 0; i < sz; i++) { + pw.println(" sfields[" + i + "]: " + staticFields.get(i)); } - /** - * Adds a virtual method. - * - * @param method {@code non-null;} the method to add - */ - public void addVirtualMethod(EncodedMethod method) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - virtualMethods.add(method); + sz = instanceFields.size(); + for (int i = 0; i < sz; i++) { + pw.println(" ifields[" + i + "]: " + instanceFields.get(i)); } - /** - * Gets all the methods in this class. The returned list is not linked - * in any way to the underlying lists contained in this instance, but - * the objects contained in the list are shared. - * - * @return {@code non-null;} list of all methods - */ - public ArrayList<EncodedMethod> getMethods() { - int sz = directMethods.size() + virtualMethods.size(); - ArrayList<EncodedMethod> result = new ArrayList<EncodedMethod>(sz); - - result.addAll(directMethods); - result.addAll(virtualMethods); - - return result; + sz = directMethods.size(); + for (int i = 0; i < sz; i++) { + pw.println(" dmeths[" + i + "]:"); + directMethods.get(i).debugPrint(pw, verbose); } - - /** - * Prints out the contents of this instance, in a debugging-friendly - * way. - * - * @param out {@code non-null;} where to output to - * @param verbose whether to be verbose with the output - */ - public void debugPrint(Writer out, boolean verbose) { - PrintWriter pw = Writers.printWriterFor(out); - - int sz = staticFields.size(); - for (int i = 0; i < sz; i++) { - pw.println(" sfields[" + i + "]: " + staticFields.get(i)); - } - - sz = instanceFields.size(); - for (int i = 0; i < sz; i++) { - pw.println(" ifields[" + i + "]: " + instanceFields.get(i)); - } - - sz = directMethods.size(); - for (int i = 0; i < sz; i++) { - pw.println(" dmeths[" + i + "]:"); - directMethods.get(i).debugPrint(pw, verbose); - } - - sz = virtualMethods.size(); - for (int i = 0; i < sz; i++) { - pw.println(" vmeths[" + i + "]:"); - virtualMethods.get(i).debugPrint(pw, verbose); - } + sz = virtualMethods.size(); + for (int i = 0; i < sz; i++) { + pw.println(" vmeths[" + i + "]:"); + virtualMethods.get(i).debugPrint(pw, verbose); + } + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + if (!staticFields.isEmpty()) { + getStaticValuesConstant(); // Force the fields to be sorted. + for (EncodedField field : staticFields) { + field.addContents(file); + } } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - if (!staticFields.isEmpty()) { - getStaticValuesConstant(); // Force the fields to be sorted. - for (EncodedField field : staticFields) { - field.addContents(file); - } - } - - if (!instanceFields.isEmpty()) { - Collections.sort(instanceFields); - for (EncodedField field : instanceFields) { - field.addContents(file); - } - } - - if (!directMethods.isEmpty()) { - Collections.sort(directMethods); - for (EncodedMethod method : directMethods) { - method.addContents(file); - } - } - - if (!virtualMethods.isEmpty()) { - Collections.sort(virtualMethods); - for (EncodedMethod method : virtualMethods) { - method.addContents(file); - } - } + if (!instanceFields.isEmpty()) { + Collections.sort(instanceFields); + for (EncodedField field : instanceFields) { + field.addContents(file); + } } - /** - * Gets a {@link CstArray} corresponding to {@link #staticValues} if - * it contains any non-zero non-{@code null} values. - * - * @return {@code null-ok;} the corresponding constant or {@code null} if - * there are no values to encode - */ - public CstArray getStaticValuesConstant() { - if ((staticValuesConstant == null) && (staticFields.size() != 0)) { - staticValuesConstant = makeStaticValuesConstant(); - } + if (!directMethods.isEmpty()) { + Collections.sort(directMethods); + for (EncodedMethod method : directMethods) { + method.addContents(file); + } + } - return staticValuesConstant; + if (!virtualMethods.isEmpty()) { + Collections.sort(virtualMethods); + for (EncodedMethod method : virtualMethods) { + method.addContents(file); + } + } + } + + /** + * Gets a {@link CstArray} corresponding to {@link #staticValues} if + * it contains any non-zero non-{@code null} values. + * + * @return {@code null-ok;} the corresponding constant or {@code null} if + * there are no values to encode + */ + public CstArray getStaticValuesConstant() { + if ((staticValuesConstant == null) && (staticFields.size() != 0)) { + staticValuesConstant = makeStaticValuesConstant(); } - /** - * Gets a {@link CstArray} corresponding to {@link #staticValues} if - * it contains any non-zero non-{@code null} values. - * - * @return {@code null-ok;} the corresponding constant or {@code null} if - * there are no values to encode + return staticValuesConstant; + } + + /** + * Gets a {@link CstArray} corresponding to {@link #staticValues} if + * it contains any non-zero non-{@code null} values. + * + * @return {@code null-ok;} the corresponding constant or {@code null} if + * there are no values to encode + */ + private CstArray makeStaticValuesConstant() { + // First sort the statics into their final order. + Collections.sort(staticFields); + + /* + * Get the size of staticValues minus any trailing zeros/nulls (both + * nulls per se as well as instances of CstKnownNull). */ - private CstArray makeStaticValuesConstant() { - // First sort the statics into their final order. - Collections.sort(staticFields); - - /* - * Get the size of staticValues minus any trailing zeros/nulls (both - * nulls per se as well as instances of CstKnownNull). - */ - - int size = staticFields.size(); - while (size > 0) { - EncodedField field = staticFields.get(size - 1); - Constant cst = staticValues.get(field); - if (cst instanceof CstLiteralBits) { - // Note: CstKnownNull extends CstLiteralBits. - if (((CstLiteralBits) cst).getLongBits() != 0) { - break; - } - } else if (cst != null) { - break; - } - size--; - } - - if (size == 0) { - return null; - } - - // There is something worth encoding, so build up a result. - CstArray.List list = new CstArray.List(size); - for (int i = 0; i < size; i++) { - EncodedField field = staticFields.get(i); - Constant cst = staticValues.get(field); - if (cst == null) { - cst = Zeroes.zeroFor(field.getRef().getType()); - } - list.set(i, cst); +int size = staticFields.size(); + while (size > 0) { + EncodedField field = staticFields.get(size - 1); + Constant cst = staticValues.get(field); + if (cst instanceof CstLiteralBits) { + // Note: CstKnownNull extends CstLiteralBits. + if (((CstLiteralBits) cst).getLongBits() != 0) { + break; } - list.setImmutable(); - - return new CstArray(list); + } else if (cst != null) { + break; + } + size--; } - /** {@inheritDoc} */ - @Override - protected void place0(Section addedTo, int offset) { - // Encode the data and note the size. - - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); - - encodeOutput(addedTo.getFile(), out); - encodedForm = out.toByteArray(); - setWriteSize(encodedForm.length); + if (size == 0) { + return null; } - /** - * Writes out the encoded form of this instance. - * - * @param file {@code non-null;} file this instance is part of - * @param out {@code non-null;} where to write to - */ - private void encodeOutput(DexFile file, AnnotatedOutput out) { - boolean annotates = out.annotates(); + // There is something worth encoding, so build up a result. - if (annotates) { - out.annotate(0, offsetString() + " class data for " + - thisClass.toHuman()); - } + CstArray.List list = new CstArray.List(size); + for (int i = 0; i < size; i++) { + EncodedField field = staticFields.get(i); + Constant cst = staticValues.get(field); + if (cst == null) { + cst = Zeroes.zeroFor(field.getRef().getType()); + } + list.set(i, cst); + } + list.setImmutable(); + + return new CstArray(list); + } + + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + // Encode the data and note the size. + + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + + encodeOutput(addedTo.getFile(), out); + encodedForm = out.toByteArray(); + setWriteSize(encodedForm.length); + } + + /** + * Writes out the encoded form of this instance. + * + * @param file {@code non-null;} file this instance is part of + * @param out {@code non-null;} where to write to + */ + private void encodeOutput(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + + if (annotates) { + out.annotate(0, offsetString() + " class data for " + thisClass.toHuman()); + } - encodeSize(file, out, "static_fields", staticFields.size()); - encodeSize(file, out, "instance_fields", instanceFields.size()); - encodeSize(file, out, "direct_methods", directMethods.size()); - encodeSize(file, out, "virtual_methods", virtualMethods.size()); + encodeSize(file, out, "static_fields", staticFields.size()); + encodeSize(file, out, "instance_fields", instanceFields.size()); + encodeSize(file, out, "direct_methods", directMethods.size()); + encodeSize(file, out, "virtual_methods", virtualMethods.size()); - encodeList(file, out, "static_fields", staticFields); - encodeList(file, out, "instance_fields", instanceFields); - encodeList(file, out, "direct_methods", directMethods); - encodeList(file, out, "virtual_methods", virtualMethods); + encodeList(file, out, "static_fields", staticFields); + encodeList(file, out, "instance_fields", instanceFields); + encodeList(file, out, "direct_methods", directMethods); + encodeList(file, out, "virtual_methods", virtualMethods); - if (annotates) { - out.endAnnotation(); - } + if (annotates) { + out.endAnnotation(); } - - /** - * Helper for {@link #encodeOutput}, which writes out the given - * size value, annotating it as well (if annotations are enabled). - * - * @param file {@code non-null;} file this instance is part of - * @param out {@code non-null;} where to write to - * @param label {@code non-null;} the label for the purposes of annotation - * @param size {@code >= 0;} the size to write - */ - private static void encodeSize(DexFile file, AnnotatedOutput out, - String label, int size) { - if (out.annotates()) { - out.annotate(String.format(" %-21s %08x", label + "_size:", - size)); - } - - out.writeUleb128(size); + } + + /** + * Helper for {@link #encodeOutput}, which writes out the given + * size value, annotating it as well (if annotations are enabled). + * + * @param file {@code non-null;} file this instance is part of + * @param out {@code non-null;} where to write to + * @param label {@code non-null;} the label for the purposes of annotation + * @param size {@code >= 0;} the size to write + */ + private static void encodeSize(DexFile file, AnnotatedOutput out, String label, int size) { + if (out.annotates()) { + out.annotate(String.format(" %-21s %08x", label + "_size:", size)); } - /** - * Helper for {@link #encodeOutput}, which writes out the given - * list. It also annotates the items (if any and if annotations - * are enabled). - * - * @param file {@code non-null;} file this instance is part of - * @param out {@code non-null;} where to write to - * @param label {@code non-null;} the label for the purposes of annotation - * @param list {@code non-null;} the list in question - */ - private static void encodeList(DexFile file, AnnotatedOutput out, - String label, ArrayList<? extends EncodedMember> list) { - int size = list.size(); - int lastIndex = 0; - - if (size == 0) { - return; - } - - if (out.annotates()) { - out.annotate(0, " " + label + ":"); - } + out.writeUleb128(size); + } + + /** + * Helper for {@link #encodeOutput}, which writes out the given + * list. It also annotates the items (if any and if annotations + * are enabled). + * + * @param file {@code non-null;} file this instance is part of + * @param out {@code non-null;} where to write to + * @param label {@code non-null;} the label for the purposes of annotation + * @param list {@code non-null;} the list in question + */ + private static void encodeList(DexFile file, AnnotatedOutput out, String label, + ArrayList<? extends EncodedMember> list) { + int size = list.size(); + int lastIndex = 0; + + if (size == 0) { + return; + } - for (int i = 0; i < size; i++) { - lastIndex = list.get(i).encode(file, out, lastIndex, i); - } + if (out.annotates()) { + out.annotate(0, " " + label + ":"); } - /** {@inheritDoc} */ - @Override - public void writeTo0(DexFile file, AnnotatedOutput out) { - boolean annotates = out.annotates(); - - if (annotates) { - /* - * The output is to be annotated, so redo the work previously - * done by place0(), except this time annotations will actually - * get emitted. - */ - encodeOutput(file, out); - } else { - out.write(encodedForm); - } + for (int i = 0; i < size; i++) { + lastIndex = list.get(i).encode(file, out, lastIndex, i); + } + } + + /** {@inheritDoc} */ + @Override + public void writeTo0(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + + if (annotates) { + /* + * The output is to be annotated, so redo the work previously + * done by place0(), except this time annotations will actually + * get emitted. + */ + encodeOutput(file, out); + } else { + out.write(encodedForm); } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/ClassDefItem.java b/dx/src/com/android/jack/dx/dex/file/ClassDefItem.java index 7f485a6..e845fab 100644 --- a/dx/src/com/android/jack/dx/dex/file/ClassDefItem.java +++ b/dx/src/com/android/jack/dx/dex/file/ClassDefItem.java @@ -43,366 +43,351 @@ import java.util.ArrayList; */ public final class ClassDefItem extends IndexedItem { - /** {@code non-null;} type constant for this class */ - private final CstType thisClass; - - /** access flags */ - private final int accessFlags; - - /** - * {@code null-ok;} superclass or {@code null} if this class is a/the - * root class - */ - private final CstType superclass; - - /** {@code null-ok;} list of implemented interfaces */ - private TypeListItem interfaces; - - /** {@code null-ok;} source file name or {@code null} if unknown */ - private final CstString sourceFile; - - /** {@code non-null;} associated class data object */ - private final ClassDataItem classData; - - /** - * {@code null-ok;} item wrapper for the static values, initialized - * in {@link #addContents} - */ - private EncodedArrayItem staticValuesItem; - - /** {@code non-null;} annotations directory */ - private AnnotationsDirectoryItem annotationsDirectory; - - /** - * Constructs an instance. Its sets of members and annotations are - * initially empty. - * - * @param thisClass {@code non-null;} type constant for this class - * @param accessFlags access flags - * @param superclass {@code null-ok;} superclass or {@code null} if - * this class is a/the root class - * @param interfaces {@code non-null;} list of implemented interfaces - * @param sourceFile {@code null-ok;} source file name or - * {@code null} if unknown - */ - public ClassDefItem(CstType thisClass, int accessFlags, - CstType superclass, TypeList interfaces, CstString sourceFile) { - if (thisClass == null) { - throw new NullPointerException("thisClass == null"); - } - - /* - * TODO: Maybe check accessFlags and superclass, at - * least for easily-checked stuff? - */ - - if (interfaces == null) { - throw new NullPointerException("interfaces == null"); - } - - this.thisClass = thisClass; - this.accessFlags = accessFlags; - this.superclass = superclass; - this.interfaces = - (interfaces.size() == 0) ? null : new TypeListItem(interfaces); - this.sourceFile = sourceFile; - this.classData = new ClassDataItem(thisClass); - this.staticValuesItem = null; - this.annotationsDirectory = new AnnotationsDirectoryItem(); - } - - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_CLASS_DEF_ITEM; - } - - /** {@inheritDoc} */ - @Override - public int writeSize() { - return SizeOf.CLASS_DEF_ITEM; - } - - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - TypeIdsSection typeIds = file.getTypeIds(); - MixedItemSection byteData = file.getByteData(); - MixedItemSection wordData = file.getWordData(); - MixedItemSection typeLists = file.getTypeLists(); - StringIdsSection stringIds = file.getStringIds(); - - typeIds.intern(thisClass); - - if (!classData.isEmpty()) { - MixedItemSection classDataSection = file.getClassData(); - classDataSection.add(classData); - - CstArray staticValues = classData.getStaticValuesConstant(); - if (staticValues != null) { - staticValuesItem = - byteData.intern(new EncodedArrayItem(staticValues)); - } - } - - if (superclass != null) { - typeIds.intern(superclass); - } - - if (interfaces != null) { - interfaces = typeLists.intern(interfaces); - } - - if (sourceFile != null) { - stringIds.intern(sourceFile); - } - - if (! annotationsDirectory.isEmpty()) { - if (annotationsDirectory.isInternable()) { - annotationsDirectory = wordData.intern(annotationsDirectory); - } else { - wordData.add(annotationsDirectory); - } - } - } - - /** {@inheritDoc} */ - @Override - public void writeTo(DexFile file, AnnotatedOutput out) { - boolean annotates = out.annotates(); - TypeIdsSection typeIds = file.getTypeIds(); - int classIdx = typeIds.indexOf(thisClass); - int superIdx = (superclass == null) ? -1 : - typeIds.indexOf(superclass); - int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces); - int annoOff = annotationsDirectory.isEmpty() ? 0 : - annotationsDirectory.getAbsoluteOffset(); - int sourceFileIdx = (sourceFile == null) ? -1 : - file.getStringIds().indexOf(sourceFile); - int dataOff = classData.isEmpty()? 0 : classData.getAbsoluteOffset(); - int staticValuesOff = - OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem); - - if (annotates) { - out.annotate(0, indexString() + ' ' + thisClass.toHuman()); - out.annotate(4, " class_idx: " + Hex.u4(classIdx)); - out.annotate(4, " access_flags: " + - AccessFlags.classString(accessFlags)); - out.annotate(4, " superclass_idx: " + Hex.u4(superIdx) + - " // " + ((superclass == null) ? "<none>" : - superclass.toHuman())); - out.annotate(4, " interfaces_off: " + Hex.u4(interOff)); - if (interOff != 0) { - TypeList list = interfaces.getList(); - int sz = list.size(); - for (int i = 0; i < sz; i++) { - out.annotate(0, " " + list.getType(i).toHuman()); - } - } - out.annotate(4, " source_file_idx: " + Hex.u4(sourceFileIdx) + - " // " + ((sourceFile == null) ? "<none>" : - sourceFile.toHuman())); - out.annotate(4, " annotations_off: " + Hex.u4(annoOff)); - out.annotate(4, " class_data_off: " + Hex.u4(dataOff)); - out.annotate(4, " static_values_off: " + - Hex.u4(staticValuesOff)); - } - - out.writeInt(classIdx); - out.writeInt(accessFlags); - out.writeInt(superIdx); - out.writeInt(interOff); - out.writeInt(sourceFileIdx); - out.writeInt(annoOff); - out.writeInt(dataOff); - out.writeInt(staticValuesOff); - } - - /** - * Gets the constant corresponding to this class. - * - * @return {@code non-null;} the constant - */ - public CstType getThisClass() { - return thisClass; + /** {@code non-null;} type constant for this class */ + private final CstType thisClass; + + /** access flags */ + private final int accessFlags; + + /** + * {@code null-ok;} superclass or {@code null} if this class is a/the + * root class + */ + private final CstType superclass; + + /** {@code null-ok;} list of implemented interfaces */ + private TypeListItem interfaces; + + /** {@code null-ok;} source file name or {@code null} if unknown */ + private final CstString sourceFile; + + /** {@code non-null;} associated class data object */ + private final ClassDataItem classData; + + /** + * {@code null-ok;} item wrapper for the static values, initialized + * in {@link #addContents} + */ + private EncodedArrayItem staticValuesItem; + + /** {@code non-null;} annotations directory */ + private AnnotationsDirectoryItem annotationsDirectory; + + /** + * Constructs an instance. Its sets of members and annotations are + * initially empty. + * + * @param thisClass {@code non-null;} type constant for this class + * @param accessFlags access flags + * @param superclass {@code null-ok;} superclass or {@code null} if + * this class is a/the root class + * @param interfaces {@code non-null;} list of implemented interfaces + * @param sourceFile {@code null-ok;} source file name or + * {@code null} if unknown + */ + public ClassDefItem(CstType thisClass, int accessFlags, CstType superclass, TypeList interfaces, + CstString sourceFile) { + if (thisClass == null) { + throw new NullPointerException("thisClass == null"); } - /** - * Gets the access flags. - * - * @return the access flags + /* + * TODO(dx team): Maybe check accessFlags and superclass, at + * least for easily-checked stuff? */ - public int getAccessFlags() { - return accessFlags; - } - - /** - * Gets the superclass. - * - * @return {@code null-ok;} the superclass or {@code null} if - * this class is a/the root class - */ - public CstType getSuperclass() { - return superclass; - } - - /** - * Gets the list of interfaces implemented. - * - * @return {@code non-null;} the interfaces list - */ - public TypeList getInterfaces() { - if (interfaces == null) { - return StdTypeList.EMPTY; - } - - return interfaces.getList(); - } - - /** - * Gets the source file name. - * - * @return {@code null-ok;} the source file name or {@code null} if unknown - */ - public CstString getSourceFile() { - return sourceFile; - } - - /** - * Adds a static field. - * - * @param field {@code non-null;} the field to add - * @param value {@code null-ok;} initial value for the field, if any - */ - public void addStaticField(EncodedField field, Constant value) { - classData.addStaticField(field, value); - } - - /** - * Adds an instance field. - * - * @param field {@code non-null;} the field to add - */ - public void addInstanceField(EncodedField field) { - classData.addInstanceField(field); - } - /** - * Adds a direct ({@code static} and/or {@code private}) method. - * - * @param method {@code non-null;} the method to add - */ - public void addDirectMethod(EncodedMethod method) { - classData.addDirectMethod(method); +if (interfaces == null) { + throw new NullPointerException("interfaces == null"); } - /** - * Adds a virtual method. - * - * @param method {@code non-null;} the method to add - */ - public void addVirtualMethod(EncodedMethod method) { - classData.addVirtualMethod(method); + this.thisClass = thisClass; + this.accessFlags = accessFlags; + this.superclass = superclass; + this.interfaces = (interfaces.size() == 0) ? null : new TypeListItem(interfaces); + this.sourceFile = sourceFile; + this.classData = new ClassDataItem(thisClass); + this.staticValuesItem = null; + this.annotationsDirectory = new AnnotationsDirectoryItem(); + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_CLASS_DEF_ITEM; + } + + /** {@inheritDoc} */ + @Override + public int writeSize() { + return SizeOf.CLASS_DEF_ITEM; + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + TypeIdsSection typeIds = file.getTypeIds(); + MixedItemSection byteData = file.getByteData(); + MixedItemSection wordData = file.getWordData(); + MixedItemSection typeLists = file.getTypeLists(); + StringIdsSection stringIds = file.getStringIds(); + + typeIds.intern(thisClass); + + if (!classData.isEmpty()) { + MixedItemSection classDataSection = file.getClassData(); + classDataSection.add(classData); + + CstArray staticValues = classData.getStaticValuesConstant(); + if (staticValues != null) { + staticValuesItem = byteData.intern(new EncodedArrayItem(staticValues)); + } } - /** - * Gets all the methods in this class. The returned list is not linked - * in any way to the underlying lists contained in this instance, but - * the objects contained in the list are shared. - * - * @return {@code non-null;} list of all methods - */ - public ArrayList<EncodedMethod> getMethods() { - return classData.getMethods(); + if (superclass != null) { + typeIds.intern(superclass); } - /** - * Sets the direct annotations on this class. These are annotations - * made on the class, per se, as opposed to on one of its members. - * It is only valid to call this method at most once per instance. - * - * @param annotations {@code non-null;} annotations to set for this class - */ - public void setClassAnnotations(Annotations annotations) { - annotationsDirectory.setClassAnnotations(annotations); + if (interfaces != null) { + interfaces = typeLists.intern(interfaces); } - /** - * Adds a field annotations item to this class. - * - * @param field {@code non-null;} field in question - * @param annotations {@code non-null;} associated annotations to add - */ - public void addFieldAnnotations(CstFieldRef field, - Annotations annotations) { - annotationsDirectory.addFieldAnnotations(field, annotations); + if (sourceFile != null) { + stringIds.intern(sourceFile); } - /** - * Adds a method annotations item to this class. - * - * @param method {@code non-null;} method in question - * @param annotations {@code non-null;} associated annotations to add - */ - public void addMethodAnnotations(CstMethodRef method, - Annotations annotations) { - annotationsDirectory.addMethodAnnotations(method, annotations); - } - - /** - * Adds a parameter annotations item to this class. - * - * @param method {@code non-null;} method in question - * @param list {@code non-null;} associated list of annotation sets to add - */ - public void addParameterAnnotations(CstMethodRef method, - AnnotationsList list) { - annotationsDirectory.addParameterAnnotations(method, list); + if (!annotationsDirectory.isEmpty()) { + if (annotationsDirectory.isInternable()) { + annotationsDirectory = wordData.intern(annotationsDirectory); + } else { + wordData.add(annotationsDirectory); + } } - - /** - * Gets the method annotations for a given method, if any. This is - * meant for use by debugging / dumping code. - * - * @param method {@code non-null;} the method - * @return {@code null-ok;} the method annotations, if any - */ - public Annotations getMethodAnnotations(CstMethodRef method) { - return annotationsDirectory.getMethodAnnotations(method); + } + + /** {@inheritDoc} */ + @Override + public void writeTo(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + TypeIdsSection typeIds = file.getTypeIds(); + int classIdx = typeIds.indexOf(thisClass); + int superIdx = (superclass == null) ? -1 : typeIds.indexOf(superclass); + int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces); + int annoOff = annotationsDirectory.isEmpty() ? 0 : annotationsDirectory.getAbsoluteOffset(); + int sourceFileIdx = (sourceFile == null) ? -1 : file.getStringIds().indexOf(sourceFile); + int dataOff = classData.isEmpty() ? 0 : classData.getAbsoluteOffset(); + int staticValuesOff = OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem); + + if (annotates) { + out.annotate(0, indexString() + ' ' + thisClass.toHuman()); + out.annotate(4, " class_idx: " + Hex.u4(classIdx)); + out.annotate(4, " access_flags: " + AccessFlags.classString(accessFlags)); + out.annotate(4, " superclass_idx: " + Hex.u4(superIdx) + " // " + + ((superclass == null) ? "<none>" : superclass.toHuman())); + out.annotate(4, " interfaces_off: " + Hex.u4(interOff)); + if (interOff != 0) { + TypeList list = interfaces.getList(); + int sz = list.size(); + for (int i = 0; i < sz; i++) { + out.annotate(0, " " + list.getType(i).toHuman()); + } + } + out.annotate(4, " source_file_idx: " + Hex.u4(sourceFileIdx) + " // " + + ((sourceFile == null) ? "<none>" : sourceFile.toHuman())); + out.annotate(4, " annotations_off: " + Hex.u4(annoOff)); + out.annotate(4, " class_data_off: " + Hex.u4(dataOff)); + out.annotate(4, " static_values_off: " + Hex.u4(staticValuesOff)); } - /** - * Gets the parameter annotations for a given method, if any. This is - * meant for use by debugging / dumping code. - * - * @param method {@code non-null;} the method - * @return {@code null-ok;} the parameter annotations, if any - */ - public AnnotationsList getParameterAnnotations(CstMethodRef method) { - return annotationsDirectory.getParameterAnnotations(method); + out.writeInt(classIdx); + out.writeInt(accessFlags); + out.writeInt(superIdx); + out.writeInt(interOff); + out.writeInt(sourceFileIdx); + out.writeInt(annoOff); + out.writeInt(dataOff); + out.writeInt(staticValuesOff); + } + + /** + * Gets the constant corresponding to this class. + * + * @return {@code non-null;} the constant + */ + public CstType getThisClass() { + return thisClass; + } + + /** + * Gets the access flags. + * + * @return the access flags + */ + public int getAccessFlags() { + return accessFlags; + } + + /** + * Gets the superclass. + * + * @return {@code null-ok;} the superclass or {@code null} if + * this class is a/the root class + */ + public CstType getSuperclass() { + return superclass; + } + + /** + * Gets the list of interfaces implemented. + * + * @return {@code non-null;} the interfaces list + */ + public TypeList getInterfaces() { + if (interfaces == null) { + return StdTypeList.EMPTY; } - /** - * Prints out the contents of this instance, in a debugging-friendly - * way. - * - * @param out {@code non-null;} where to output to - * @param verbose whether to be verbose with the output - */ - public void debugPrint(Writer out, boolean verbose) { - PrintWriter pw = Writers.printWriterFor(out); - - pw.println(getClass().getName() + " {"); - pw.println(" accessFlags: " + Hex.u2(accessFlags)); - pw.println(" superclass: " + superclass); - pw.println(" interfaces: " + - ((interfaces == null) ? "<none>" : interfaces)); - pw.println(" sourceFile: " + - ((sourceFile == null) ? "<none>" : sourceFile.toQuoted())); - - classData.debugPrint(out, verbose); - annotationsDirectory.debugPrint(pw); - - pw.println("}"); - } + return interfaces.getList(); + } + + /** + * Gets the source file name. + * + * @return {@code null-ok;} the source file name or {@code null} if unknown + */ + public CstString getSourceFile() { + return sourceFile; + } + + /** + * Adds a static field. + * + * @param field {@code non-null;} the field to add + * @param value {@code null-ok;} initial value for the field, if any + */ + public void addStaticField(EncodedField field, Constant value) { + classData.addStaticField(field, value); + } + + /** + * Adds an instance field. + * + * @param field {@code non-null;} the field to add + */ + public void addInstanceField(EncodedField field) { + classData.addInstanceField(field); + } + + /** + * Adds a direct ({@code static} and/or {@code private}) method. + * + * @param method {@code non-null;} the method to add + */ + public void addDirectMethod(EncodedMethod method) { + classData.addDirectMethod(method); + } + + /** + * Adds a virtual method. + * + * @param method {@code non-null;} the method to add + */ + public void addVirtualMethod(EncodedMethod method) { + classData.addVirtualMethod(method); + } + + /** + * Gets all the methods in this class. The returned list is not linked + * in any way to the underlying lists contained in this instance, but + * the objects contained in the list are shared. + * + * @return {@code non-null;} list of all methods + */ + public ArrayList<EncodedMethod> getMethods() { + return classData.getMethods(); + } + + /** + * Sets the direct annotations on this class. These are annotations + * made on the class, per se, as opposed to on one of its members. + * It is only valid to call this method at most once per instance. + * + * @param annotations {@code non-null;} annotations to set for this class + */ + public void setClassAnnotations(Annotations annotations) { + annotationsDirectory.setClassAnnotations(annotations); + } + + /** + * Adds a field annotations item to this class. + * + * @param field {@code non-null;} field in question + * @param annotations {@code non-null;} associated annotations to add + */ + public void addFieldAnnotations(CstFieldRef field, Annotations annotations) { + annotationsDirectory.addFieldAnnotations(field, annotations); + } + + /** + * Adds a method annotations item to this class. + * + * @param method {@code non-null;} method in question + * @param annotations {@code non-null;} associated annotations to add + */ + public void addMethodAnnotations(CstMethodRef method, Annotations annotations) { + annotationsDirectory.addMethodAnnotations(method, annotations); + } + + /** + * Adds a parameter annotations item to this class. + * + * @param method {@code non-null;} method in question + * @param list {@code non-null;} associated list of annotation sets to add + */ + public void addParameterAnnotations(CstMethodRef method, AnnotationsList list) { + annotationsDirectory.addParameterAnnotations(method, list); + } + + /** + * Gets the method annotations for a given method, if any. This is + * meant for use by debugging / dumping code. + * + * @param method {@code non-null;} the method + * @return {@code null-ok;} the method annotations, if any + */ + public Annotations getMethodAnnotations(CstMethodRef method) { + return annotationsDirectory.getMethodAnnotations(method); + } + + /** + * Gets the parameter annotations for a given method, if any. This is + * meant for use by debugging / dumping code. + * + * @param method {@code non-null;} the method + * @return {@code null-ok;} the parameter annotations, if any + */ + public AnnotationsList getParameterAnnotations(CstMethodRef method) { + return annotationsDirectory.getParameterAnnotations(method); + } + + /** + * Prints out the contents of this instance, in a debugging-friendly + * way. + * + * @param out {@code non-null;} where to output to + * @param verbose whether to be verbose with the output + */ + public void debugPrint(Writer out, boolean verbose) { + PrintWriter pw = Writers.printWriterFor(out); + + pw.println(getClass().getName() + " {"); + pw.println(" accessFlags: " + Hex.u2(accessFlags)); + pw.println(" superclass: " + superclass); + pw.println(" interfaces: " + ((interfaces == null) ? "<none>" : interfaces)); + pw.println(" sourceFile: " + ((sourceFile == null) ? "<none>" : sourceFile.toQuoted())); + + classData.debugPrint(out, verbose); + annotationsDirectory.debugPrint(pw); + + pw.println("}"); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/ClassDefsSection.java b/dx/src/com/android/jack/dx/dex/file/ClassDefsSection.java index 512ffc1..0c66c50 100644 --- a/dx/src/com/android/jack/dx/dex/file/ClassDefsSection.java +++ b/dx/src/com/android/jack/dx/dex/file/ClassDefsSection.java @@ -31,157 +31,157 @@ import java.util.TreeMap; * Class definitions list section of a {@code .dex} file. */ public final class ClassDefsSection extends UniformItemSection { - /** - * {@code non-null;} map from type constants for classes to {@link - * ClassDefItem} instances that define those classes - */ - private final TreeMap<Type, ClassDefItem> classDefs; - - /** {@code null-ok;} ordered list of classes; set in {@link #orderItems} */ - private ArrayList<ClassDefItem> orderedDefs; + /** + * {@code non-null;} map from type constants for classes to {@link + * ClassDefItem} instances that define those classes + */ + private final TreeMap<Type, ClassDefItem> classDefs; + + /** {@code null-ok;} ordered list of classes; set in {@link #orderItems} */ + private ArrayList<ClassDefItem> orderedDefs; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param file {@code non-null;} file that this instance is part of + */ + public ClassDefsSection(DexFile file) { + super("class_defs", file, 4); + + classDefs = new TreeMap<Type, ClassDefItem>(); + orderedDefs = null; + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + if (orderedDefs != null) { + return orderedDefs; + } - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param file {@code non-null;} file that this instance is part of - */ - public ClassDefsSection(DexFile file) { - super("class_defs", file, 4); + return classDefs.values(); + } - classDefs = new TreeMap<Type, ClassDefItem>(); - orderedDefs = null; + /** {@inheritDoc} */ + @Override + public IndexedItem get(Constant cst) { + if (cst == null) { + throw new NullPointerException("cst == null"); } - /** {@inheritDoc} */ - @Override - public Collection<? extends Item> items() { - if (orderedDefs != null) { - return orderedDefs; - } + throwIfNotPrepared(); - return classDefs.values(); - } + Type type = ((CstType) cst).getClassType(); + IndexedItem result = classDefs.get(type); - /** {@inheritDoc} */ - @Override - public IndexedItem get(Constant cst) { - if (cst == null) { - throw new NullPointerException("cst == null"); - } + if (result == null) { + throw new IllegalArgumentException("not found"); + } - throwIfNotPrepared(); + return result; + } - Type type = ((CstType) cst).getClassType(); - IndexedItem result = classDefs.get(type); + /** + * Writes the portion of the file header that refers to this instance. + * + * @param out {@code non-null;} where to write + */ + public void writeHeaderPart(AnnotatedOutput out) { + throwIfNotPrepared(); - if (result == null) { - throw new IllegalArgumentException("not found"); - } + int sz = classDefs.size(); + int offset = (sz == 0) ? 0 : getFileOffset(); - return result; + if (out.annotates()) { + out.annotate(4, "class_defs_size: " + Hex.u4(sz)); + out.annotate(4, "class_defs_off: " + Hex.u4(offset)); } - /** - * Writes the portion of the file header that refers to this instance. - * - * @param out {@code non-null;} where to write - */ - public void writeHeaderPart(AnnotatedOutput out) { - throwIfNotPrepared(); - - int sz = classDefs.size(); - int offset = (sz == 0) ? 0 : getFileOffset(); + out.writeInt(sz); + out.writeInt(offset); + } + + /** + * Adds an element to this instance. It is illegal to attempt to add more + * than one class with the same name. + * + * @param clazz {@code non-null;} the class def to add + */ + public void add(ClassDefItem clazz) { + Type type; + + try { + type = clazz.getThisClass().getClassType(); + } catch (NullPointerException ex) { + // Elucidate the exception. + throw new NullPointerException("clazz == null"); + } - if (out.annotates()) { - out.annotate(4, "class_defs_size: " + Hex.u4(sz)); - out.annotate(4, "class_defs_off: " + Hex.u4(offset)); - } + throwIfPrepared(); - out.writeInt(sz); - out.writeInt(offset); + if (classDefs.get(type) != null) { + throw new IllegalArgumentException("already added: " + type); } - /** - * Adds an element to this instance. It is illegal to attempt to add more - * than one class with the same name. - * - * @param clazz {@code non-null;} the class def to add - */ - public void add(ClassDefItem clazz) { - Type type; + classDefs.put(type, clazz); + } - try { - type = clazz.getThisClass().getClassType(); - } catch (NullPointerException ex) { - // Elucidate the exception. - throw new NullPointerException("clazz == null"); - } + /** {@inheritDoc} */ + @Override + protected void orderItems() { + int sz = classDefs.size(); + int idx = 0; - throwIfPrepared(); + orderedDefs = new ArrayList<ClassDefItem>(sz); - if (classDefs.get(type) != null) { - throw new IllegalArgumentException("already added: " + type); - } + /* + * Iterate over all the classes, recursively assigning an + * index to each, implicitly skipping the ones that have + * already been assigned by the time this (top-level) + * iteration reaches them. + */ + for (Type type : classDefs.keySet()) { + idx = orderItems0(type, idx, sz - idx); + } + } + + /** + * Helper for {@link #orderItems}, which recursively assigns indices + * to classes. + * + * @param type {@code null-ok;} type ref to assign, if any + * @param idx {@code >= 0;} the next index to assign + * @param maxDepth maximum recursion depth; if negative, this will + * throw an exception indicating class definition circularity + * @return {@code >= 0;} the next index to assign + */ + private int orderItems0(Type type, int idx, int maxDepth) { + ClassDefItem c = classDefs.get(type); + + if ((c == null) || (c.hasIndex())) { + return idx; + } - classDefs.put(type, clazz); + if (maxDepth < 0) { + throw new RuntimeException("class circularity with " + type); } - /** {@inheritDoc} */ - @Override - protected void orderItems() { - int sz = classDefs.size(); - int idx = 0; - - orderedDefs = new ArrayList<ClassDefItem>(sz); - - /* - * Iterate over all the classes, recursively assigning an - * index to each, implicitly skipping the ones that have - * already been assigned by the time this (top-level) - * iteration reaches them. - */ - for (Type type : classDefs.keySet()) { - idx = orderItems0(type, idx, sz - idx); - } + maxDepth--; + + CstType superclassCst = c.getSuperclass(); + if (superclassCst != null) { + Type superclass = superclassCst.getClassType(); + idx = orderItems0(superclass, idx, maxDepth); } - /** - * Helper for {@link #orderItems}, which recursively assigns indices - * to classes. - * - * @param type {@code null-ok;} type ref to assign, if any - * @param idx {@code >= 0;} the next index to assign - * @param maxDepth maximum recursion depth; if negative, this will - * throw an exception indicating class definition circularity - * @return {@code >= 0;} the next index to assign - */ - private int orderItems0(Type type, int idx, int maxDepth) { - ClassDefItem c = classDefs.get(type); - - if ((c == null) || (c.hasIndex())) { - return idx; - } - - if (maxDepth < 0) { - throw new RuntimeException("class circularity with " + type); - } - - maxDepth--; - - CstType superclassCst = c.getSuperclass(); - if (superclassCst != null) { - Type superclass = superclassCst.getClassType(); - idx = orderItems0(superclass, idx, maxDepth); - } - - TypeList interfaces = c.getInterfaces(); - int sz = interfaces.size(); - for (int i = 0; i < sz; i++) { - idx = orderItems0(interfaces.getType(i), idx, maxDepth); - } - - c.setIndex(idx); - orderedDefs.add(c); - return idx + 1; + TypeList interfaces = c.getInterfaces(); + int sz = interfaces.size(); + for (int i = 0; i < sz; i++) { + idx = orderItems0(interfaces.getType(i), idx, maxDepth); } + + c.setIndex(idx); + orderedDefs.add(c); + return idx + 1; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/CodeItem.java b/dx/src/com/android/jack/dx/dex/file/CodeItem.java index 15cb5a8..2dbaed9 100644 --- a/dx/src/com/android/jack/dx/dex/file/CodeItem.java +++ b/dx/src/com/android/jack/dx/dex/file/CodeItem.java @@ -35,290 +35,288 @@ import java.io.PrintWriter; */ public final class CodeItem extends OffsettedItem implements Code { - /** {@code non-null;} method that this code implements */ - private final CstMethodRef ref; - - /** {@code non-null;} the bytecode instructions and associated data */ - private final DalvCode code; - - /** {@code null-ok;} the catches, if needed; set in {@link #addContents} */ - private CatchStructs catches; - - /** whether this instance is for a {@code static} method */ - private final boolean isStatic; - - /** - * {@code non-null;} list of possibly-thrown exceptions; just used in - * generating debugging output (listings) - */ - private final TypeList throwsList; - - /** - * {@code null-ok;} the debug info or {@code null} if there is none; - * set in {@link #addContents} - */ - private DebugInfoItem debugInfo; - - /** - * Constructs an instance. - * - * @param ref {@code non-null;} method that this code implements - * @param code {@code non-null;} the underlying code - * @param isStatic whether this instance is for a {@code static} - * method - * @param throwsList {@code non-null;} list of possibly-thrown exceptions, - * just used in generating debugging output (listings) - */ - public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic, - TypeList throwsList) { - super(ALIGNMENT, -1); - - if (ref == null) { - throw new NullPointerException("ref == null"); - } - - if (code == null) { - throw new NullPointerException("code == null"); - } - - if (throwsList == null) { - throw new NullPointerException("throwsList == null"); - } - - this.ref = ref; - this.code = code; - this.isStatic = isStatic; - this.throwsList = throwsList; - this.catches = null; - this.debugInfo = null; + /** {@code non-null;} method that this code implements */ + private final CstMethodRef ref; + + /** {@code non-null;} the bytecode instructions and associated data */ + private final DalvCode code; + + /** {@code null-ok;} the catches, if needed; set in {@link #addContents} */ + private CatchStructs catches; + + /** whether this instance is for a {@code static} method */ + private final boolean isStatic; + + /** + * {@code non-null;} list of possibly-thrown exceptions; just used in + * generating debugging output (listings) + */ + private final TypeList throwsList; + + /** + * {@code null-ok;} the debug info or {@code null} if there is none; + * set in {@link #addContents} + */ + private DebugInfoItem debugInfo; + + /** + * Constructs an instance. + * + * @param ref {@code non-null;} method that this code implements + * @param code {@code non-null;} the underlying code + * @param isStatic whether this instance is for a {@code static} + * method + * @param throwsList {@code non-null;} list of possibly-thrown exceptions, + * just used in generating debugging output (listings) + */ + public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic, TypeList throwsList) { + super(ALIGNMENT, -1); + + if (ref == null) { + throw new NullPointerException("ref == null"); } - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_CODE_ITEM; + if (code == null) { + throw new NullPointerException("code == null"); } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - MixedItemSection byteData = file.getByteData(); - TypeIdsSection typeIds = file.getTypeIds(); - - if (code.hasPositions() || code.hasLocals()) { - debugInfo = new DebugInfoItem(code, isStatic, ref); - byteData.add(debugInfo); - } - - if (code.hasAnyCatches()) { - for (Type type : code.getCatchTypes()) { - typeIds.intern(type); - } - catches = new CatchStructs(code); - } - - for (Constant c : code.getInsnConstants()) { - file.internIfAppropriate(c); - } + if (throwsList == null) { + throw new NullPointerException("throwsList == null"); } - /** {@inheritDoc} */ - @Override - public String toString() { - return "CodeItem{" + toHuman() + "}"; + this.ref = ref; + this.code = code; + this.isStatic = isStatic; + this.throwsList = throwsList; + this.catches = null; + this.debugInfo = null; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_CODE_ITEM; + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + MixedItemSection byteData = file.getByteData(); + TypeIdsSection typeIds = file.getTypeIds(); + + if (code.hasPositions() || code.hasLocals()) { + debugInfo = new DebugInfoItem(code, isStatic, ref); + byteData.add(debugInfo); } - /** {@inheritDoc} */ - @Override - public String toHuman() { - return ref.toHuman(); + if (code.hasAnyCatches()) { + for (Type type : code.getCatchTypes()) { + typeIds.intern(type); + } + catches = new CatchStructs(code); } - /** - * Gets the reference to the method this instance implements. - * - * @return {@code non-null;} the method reference - */ - public CstMethodRef getRef() { - return ref; + for (Constant c : code.getInsnConstants()) { + file.internIfAppropriate(c); } - - /** - * Does a human-friendly dump of this instance. - * - * @param out {@code non-null;} where to dump - * @param prefix {@code non-null;} per-line prefix to use - * @param verbose whether to be verbose with the output - */ - @Override - public void debugPrint(PrintWriter out, String prefix, boolean verbose) { - out.println(ref.toHuman() + ":"); - - DalvInsnList insns = code.getInsns(); - out.println("regs: " + Hex.u2(getRegistersSize()) + - "; ins: " + Hex.u2(getInsSize()) + "; outs: " + - Hex.u2(getOutsSize())); - - insns.debugPrint(out, prefix, verbose); - - String prefix2 = prefix + " "; - - if (catches != null) { - out.print(prefix); - out.println("catches"); - catches.debugPrint(out, prefix2); - } - - if (debugInfo != null) { - out.print(prefix); - out.println("debug info"); - debugInfo.debugPrint(out, prefix2); - } + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "CodeItem{" + toHuman() + "}"; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return ref.toHuman(); + } + + /** + * Gets the reference to the method this instance implements. + * + * @return {@code non-null;} the method reference + */ + public CstMethodRef getRef() { + return ref; + } + + /** + * Does a human-friendly dump of this instance. + * + * @param out {@code non-null;} where to dump + * @param prefix {@code non-null;} per-line prefix to use + * @param verbose whether to be verbose with the output + */ + @Override + public void debugPrint(PrintWriter out, String prefix, boolean verbose) { + out.println(ref.toHuman() + ":"); + + DalvInsnList insns = code.getInsns(); + out.println("regs: " + Hex.u2(getRegistersSize()) + "; ins: " + Hex.u2(getInsSize()) + + "; outs: " + Hex.u2(getOutsSize())); + + insns.debugPrint(out, prefix, verbose); + + String prefix2 = prefix + " "; + + if (catches != null) { + out.print(prefix); + out.println("catches"); + catches.debugPrint(out, prefix2); } - /** {@inheritDoc} */ - @Override - protected void place0(Section addedTo, int offset) { - final DexFile file = addedTo.getFile(); - int catchesSize; - - /* - * In order to get the catches and insns, all the code's - * constants need to be assigned indices. - */ - code.assignIndices(new DalvCode.AssignIndicesCallback() { - @Override - public int getIndex(Constant cst) { - IndexedItem item = file.findItemOrNull(cst); - if (item == null) { - return -1; - } - return item.getIndex(); - } - }); - - if (catches != null) { - catches.encode(file); - catchesSize = catches.writeSize(); - } else { - catchesSize = 0; - } + if (debugInfo != null) { + out.print(prefix); + out.println("debug info"); + debugInfo.debugPrint(out, prefix2); + } + } - /* - * The write size includes the header, two bytes per code - * unit, post-code padding if necessary, and however much - * space the catches need. - */ + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + final DexFile file = addedTo.getFile(); + int catchesSize; - int insnsSize = code.getInsns().codeSize(); - if ((insnsSize & 1) != 0) { - insnsSize++; + /* + * In order to get the catches and insns, all the code's + * constants need to be assigned indices. + */ + code.assignIndices(new DalvCode.AssignIndicesCallback() { + @Override + public int getIndex(Constant cst) { + IndexedItem item = file.findItemOrNull(cst); + if (item == null) { + return -1; } - - setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize); + return item.getIndex(); + } + }); + + if (catches != null) { + catches.encode(file); + catchesSize = catches.writeSize(); + } else { + catchesSize = 0; } - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - boolean annotates = out.annotates(); - int regSz = getRegistersSize(); - int outsSz = getOutsSize(); - int insSz = getInsSize(); - int insnsSz = code.getInsns().codeSize(); - boolean needPadding = (insnsSz & 1) != 0; - int triesSz = (catches == null) ? 0 : catches.triesSize(); - int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset(); - - if (annotates) { - out.annotate(0, offsetString() + ' ' + ref.toHuman()); - out.annotate(2, " registers_size: " + Hex.u2(regSz)); - out.annotate(2, " ins_size: " + Hex.u2(insSz)); - out.annotate(2, " outs_size: " + Hex.u2(outsSz)); - out.annotate(2, " tries_size: " + Hex.u2(triesSz)); - out.annotate(4, " debug_off: " + Hex.u4(debugOff)); - out.annotate(4, " insns_size: " + Hex.u4(insnsSz)); - - // This isn't represented directly here, but it is useful to see. - int size = throwsList.size(); - if (size != 0) { - out.annotate(0, " throws " + StdTypeList.toHuman(throwsList)); - } - } + /* + * The write size includes the header, two bytes per code + * unit, post-code padding if necessary, and however much + * space the catches need. + */ - out.writeShort(regSz); - out.writeShort(insSz); - out.writeShort(outsSz); - out.writeShort(triesSz); - out.writeInt(debugOff); - out.writeInt(insnsSz); +int insnsSize = code.getInsns().codeSize(); + if ((insnsSize & 1) != 0) { + insnsSize++; + } - writeCodes(file, out); + setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize); + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + int regSz = getRegistersSize(); + int outsSz = getOutsSize(); + int insSz = getInsSize(); + int insnsSz = code.getInsns().codeSize(); + boolean needPadding = (insnsSz & 1) != 0; + int triesSz = (catches == null) ? 0 : catches.triesSize(); + int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset(); + + if (annotates) { + out.annotate(0, offsetString() + ' ' + ref.toHuman()); + out.annotate(2, " registers_size: " + Hex.u2(regSz)); + out.annotate(2, " ins_size: " + Hex.u2(insSz)); + out.annotate(2, " outs_size: " + Hex.u2(outsSz)); + out.annotate(2, " tries_size: " + Hex.u2(triesSz)); + out.annotate(4, " debug_off: " + Hex.u4(debugOff)); + out.annotate(4, " insns_size: " + Hex.u4(insnsSz)); + + // This isn't represented directly here, but it is useful to see. + int size = throwsList.size(); + if (size != 0) { + out.annotate(0, " throws " + StdTypeList.toHuman(throwsList)); + } + } - if (catches != null) { - if (needPadding) { - if (annotates) { - out.annotate(2, " padding: 0"); - } - out.writeShort(0); - } + out.writeShort(regSz); + out.writeShort(insSz); + out.writeShort(outsSz); + out.writeShort(triesSz); + out.writeInt(debugOff); + out.writeInt(insnsSz); - catches.writeTo(file, out); - } + writeCodes(file, out); + if (catches != null) { + if (needPadding) { if (annotates) { - /* - * These are pointed at in the code header (above), but it's less - * distracting to expand on them at the bottom of the code. - */ - if (debugInfo != null) { - out.annotate(0, " debug info"); - debugInfo.annotateTo(file, out, " "); - } - } - } - - /** - * Helper for {@link #writeTo0} which writes out the actual bytecode. - * - * @param file {@code non-null;} file we are part of - * @param out {@code non-null;} where to write to - */ - private void writeCodes(DexFile file, AnnotatedOutput out) { - DalvInsnList insns = code.getInsns(); - - try { - insns.writeTo(out); - } catch (RuntimeException ex) { - throw ExceptionWithContext.withContext(ex, "...while writing " + - "instructions for " + ref.toHuman()); + out.annotate(2, " padding: 0"); } - } + out.writeShort(0); + } - /** - * Get the in registers count. - * - * @return the count - */ - private int getInsSize() { - return ref.getParameterWordCount(isStatic); + catches.writeTo(file, out); } - /** - * Get the out registers count. - * - * @return the count - */ - private int getOutsSize() { - return code.getInsns().getOutsSize(); + if (annotates) { + /* + * These are pointed at in the code header (above), but it's less + * distracting to expand on them at the bottom of the code. + */ + if (debugInfo != null) { + out.annotate(0, " debug info"); + debugInfo.annotateTo(file, out, " "); + } } - - /** - * Get the total registers count. - * - * @return the count - */ - private int getRegistersSize() { - return code.getInsns().getRegistersSize(); + } + + /** + * Helper for {@link #writeTo0} which writes out the actual bytecode. + * + * @param file {@code non-null;} file we are part of + * @param out {@code non-null;} where to write to + */ + private void writeCodes(DexFile file, AnnotatedOutput out) { + DalvInsnList insns = code.getInsns(); + + try { + insns.writeTo(out); + } catch (RuntimeException ex) { + throw ExceptionWithContext.withContext(ex, + "...while writing " + "instructions for " + ref.toHuman()); } + } + + /** + * Get the in registers count. + * + * @return the count + */ + private int getInsSize() { + return ref.getParameterWordCount(isStatic); + } + + /** + * Get the out registers count. + * + * @return the count + */ + private int getOutsSize() { + return code.getInsns().getOutsSize(); + } + + /** + * Get the total registers count. + * + * @return the count + */ + private int getRegistersSize() { + return code.getInsns().getRegistersSize(); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/DebugInfoConstants.java b/dx/src/com/android/jack/dx/dex/file/DebugInfoConstants.java index 63d8a34..1e2d520 100644 --- a/dx/src/com/android/jack/dx/dex/file/DebugInfoConstants.java +++ b/dx/src/com/android/jack/dx/dex/file/DebugInfoConstants.java @@ -21,134 +21,134 @@ package com.android.jack.dx.dex.file; */ public interface DebugInfoConstants { - /* - * normal opcodes - */ - - /** - * Terminates a debug info sequence for a method.<p> - * Args: none - * - */ - static final int DBG_END_SEQUENCE = 0x00; - - /** - * Advances the program counter/address register without emitting - * a positions entry.<p> - * - * Args: - * <ol> - * <li>Unsigned LEB128 — amount to advance pc by - * </ol> - */ - static final int DBG_ADVANCE_PC = 0x01; - - /** - * Advances the line register without emitting - * a positions entry.<p> - * - * Args: - * <ol> - * <li>Signed LEB128 — amount to change line register by. - * </ol> - */ - static final int DBG_ADVANCE_LINE = 0x02; - - /** - * Introduces a local variable at the current address.<p> - * - * Args: - * <ol> - * <li>Unsigned LEB128 — register that will contain local. - * <li>Unsigned LEB128 — string index (shifted by 1) of local name. - * <li>Unsigned LEB128 — type index (shifted by 1) of type. - * </ol> - */ - static final int DBG_START_LOCAL = 0x03; - - /** - * Introduces a local variable at the current address with a type - * signature specified.<p> - * - * Args: - * <ol> - * <li>Unsigned LEB128 — register that will contain local. - * <li>Unsigned LEB128 — string index (shifted by 1) of local name. - * <li>Unsigned LEB128 — type index (shifted by 1) of type. - * <li>Unsigned LEB128 — string index (shifted by 1) of - * type signature. - * </ol> - */ - static final int DBG_START_LOCAL_EXTENDED = 0x04; - - /** - * Marks a currently-live local variable as out of scope at the - * current address.<p> - * - * Args: - * <ol> - * <li>Unsigned LEB128 — register that contained local - * </ol> - */ - static final int DBG_END_LOCAL = 0x05; - - /** - * Re-introduces a local variable at the current address. The name - * and type are the same as the last local that was live in the specified - * register.<p> - * - * Args: - * <ol> - * <li>Unsigned LEB128 — register to re-start. - * </ol> - */ - static final int DBG_RESTART_LOCAL = 0x06; - - - /** - * Sets the "prologue_end" state machine register, indicating that the - * next position entry that is added should be considered the end of - * a method prologue (an appropriate place for a method breakpoint).<p> - * - * The prologue_end register is cleared by any special - * ({@code >= OPCODE_BASE}) opcode. - */ - static final int DBG_SET_PROLOGUE_END = 0x07; - - /** - * Sets the "epilogue_begin" state machine register, indicating that the - * next position entry that is added should be considered the beginning of - * a method epilogue (an appropriate place to suspend execution before - * method exit).<p> - * - * The epilogue_begin register is cleared by any special - * ({@code >= OPCODE_BASE}) opcode. - */ - static final int DBG_SET_EPILOGUE_BEGIN = 0x08; - - /** - * Sets the current file that that line numbers refer to. All subsequent - * line number entries make reference to this source file name, instead - * of the default name specified in code_item. - * - * Args: - * <ol> - * <li>Unsigned LEB128 — string index (shifted by 1) of source - * file name. - * </ol> - */ - static final int DBG_SET_FILE = 0x09; - - /* IF YOU ADD A NEW OPCODE, increase OPCODE_BASE */ - - /* - * "special opcode" configuration, essentially what's found in - * the line number program header in DWARFv3, Section 6.2.4 - */ - - /** the smallest value a special opcode can take */ - static final int DBG_FIRST_SPECIAL = 0x0a; - static final int DBG_LINE_BASE = -4; - static final int DBG_LINE_RANGE = 15; - // MIN_INSN_LENGTH is always 1 + /* + * normal opcodes + */ + + /** + * Terminates a debug info sequence for a method.<p> + * Args: none + * + */ + static final int DBG_END_SEQUENCE = 0x00; + + /** + * Advances the program counter/address register without emitting + * a positions entry.<p> + * + * Args: + * <ol> + * <li>Unsigned LEB128 — amount to advance pc by + * </ol> + */ + static final int DBG_ADVANCE_PC = 0x01; + + /** + * Advances the line register without emitting + * a positions entry.<p> + * + * Args: + * <ol> + * <li>Signed LEB128 — amount to change line register by. + * </ol> + */ + static final int DBG_ADVANCE_LINE = 0x02; + + /** + * Introduces a local variable at the current address.<p> + * + * Args: + * <ol> + * <li>Unsigned LEB128 — register that will contain local. + * <li>Unsigned LEB128 — string index (shifted by 1) of local name. + * <li>Unsigned LEB128 — type index (shifted by 1) of type. + * </ol> + */ + static final int DBG_START_LOCAL = 0x03; + + /** + * Introduces a local variable at the current address with a type + * signature specified.<p> + * + * Args: + * <ol> + * <li>Unsigned LEB128 — register that will contain local. + * <li>Unsigned LEB128 — string index (shifted by 1) of local name. + * <li>Unsigned LEB128 — type index (shifted by 1) of type. + * <li>Unsigned LEB128 — string index (shifted by 1) of + * type signature. + * </ol> + */ + static final int DBG_START_LOCAL_EXTENDED = 0x04; + + /** + * Marks a currently-live local variable as out of scope at the + * current address.<p> + * + * Args: + * <ol> + * <li>Unsigned LEB128 — register that contained local + * </ol> + */ + static final int DBG_END_LOCAL = 0x05; + + /** + * Re-introduces a local variable at the current address. The name + * and type are the same as the last local that was live in the specified + * register.<p> + * + * Args: + * <ol> + * <li>Unsigned LEB128 — register to re-start. + * </ol> + */ + static final int DBG_RESTART_LOCAL = 0x06; + + + /** + * Sets the "prologue_end" state machine register, indicating that the + * next position entry that is added should be considered the end of + * a method prologue (an appropriate place for a method breakpoint).<p> + * + * The prologue_end register is cleared by any special + * ({@code >= OPCODE_BASE}) opcode. + */ + static final int DBG_SET_PROLOGUE_END = 0x07; + + /** + * Sets the "epilogue_begin" state machine register, indicating that the + * next position entry that is added should be considered the beginning of + * a method epilogue (an appropriate place to suspend execution before + * method exit).<p> + * + * The epilogue_begin register is cleared by any special + * ({@code >= OPCODE_BASE}) opcode. + */ + static final int DBG_SET_EPILOGUE_BEGIN = 0x08; + + /** + * Sets the current file that that line numbers refer to. All subsequent + * line number entries make reference to this source file name, instead + * of the default name specified in code_item. + * + * Args: + * <ol> + * <li>Unsigned LEB128 — string index (shifted by 1) of source + * file name. + * </ol> + */ + static final int DBG_SET_FILE = 0x09; + + /* IF YOU ADD A NEW OPCODE, increase OPCODE_BASE */ + + /* + * "special opcode" configuration, essentially what's found in + * the line number program header in DWARFv3, Section 6.2.4 + */ + + /** the smallest value a special opcode can take */ + static final int DBG_FIRST_SPECIAL = 0x0a; + static final int DBG_LINE_BASE = -4; + static final int DBG_LINE_RANGE = 15; + // MIN_INSN_LENGTH is always 1 } diff --git a/dx/src/com/android/jack/dx/dex/file/DebugInfoDecoder.java b/dx/src/com/android/jack/dx/dex/file/DebugInfoDecoder.java index 98ebc08..acd7939 100644 --- a/dx/src/com/android/jack/dx/dex/file/DebugInfoDecoder.java +++ b/dx/src/com/android/jack/dx/dex/file/DebugInfoDecoder.java @@ -16,6 +16,20 @@ package com.android.jack.dx.dex.file; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_ADVANCE_LINE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_ADVANCE_PC; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_END_LOCAL; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_END_SEQUENCE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_FIRST_SPECIAL; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_LINE_BASE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_LINE_RANGE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_RESTART_LOCAL; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_SET_EPILOGUE_BEGIN; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_SET_FILE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_SET_PROLOGUE_END; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_START_LOCAL; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_START_LOCAL_EXTENDED; + import com.android.jack.dx.dex.code.DalvCode; import com.android.jack.dx.dex.code.DalvInsnList; import com.android.jack.dx.dex.code.LocalList; @@ -35,563 +49,560 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import static com.android.jack.dx.dex.file.DebugInfoConstants.*; - /** * A decoder for the dex debug info state machine format. * This code exists mostly as a reference implementation and test for * for the {@code DebugInfoEncoder} */ public class DebugInfoDecoder { - /** encoded debug info */ - private final ByteInput encoded; + /** encoded debug info */ + private final ByteInput encoded; + + /** positions decoded */ + private final ArrayList<PositionEntry> positions; + + /** locals decoded */ + private final ArrayList<LocalEntry> locals; + + /** indexed by register, the last local variable live in a reg */ + private final LocalEntry[] lastEntryForReg; + + /** method descriptor of method this debug info is for */ + private final Prototype desc; + + /** true if method is static */ + private final boolean isStatic; + + /** + * register size, in register units, of the register space + * used by this method + */ + private final int regSize; + + /** current decoding state: line number */ + private int line = 1; + + /** current decoding state: bytecode address */ + private int address = 0; + + /** string index of the string "this" */ + private final int thisStringIdx; + + /** + * Constructs an instance. + * + * @param encoded encoded debug info + * @param regSize register size, in register units, of the register space + * used by this method + * @param isStatic true if method is static + * @param ref method descriptor of method this debug info is for + * @param file dex file this debug info will be stored in + */ + DebugInfoDecoder(byte[] encoded, int regSize, boolean isStatic, CstMethodRef ref, DexFile file) { + this(new ByteArrayByteInput(encoded), regSize, isStatic, ref.getPrototype(), extractThisIdx( + file)); + } + + /** + * Constructs an instance. + * + * @param encoded encoded debug info + * @param regSize register size, in register units, of the register space + * used by this method + * @param isStatic true if method is static + * @param desc method descriptor of method this debug info is for + * @param thisIdx string index of the string "this" or -1 if the string does not exists in the + * dex. + */ + public DebugInfoDecoder(ByteInput encoded, int regSize, boolean isStatic, Prototype desc, + int thisIdx) { + if (encoded == null) { + throw new NullPointerException("encoded == null"); + } - /** positions decoded */ - private final ArrayList<PositionEntry> positions; + this.encoded = encoded; + this.isStatic = isStatic; + this.desc = desc; + this.regSize = regSize; - /** locals decoded */ - private final ArrayList<LocalEntry> locals; + positions = new ArrayList<PositionEntry>(); + locals = new ArrayList<LocalEntry>(); + lastEntryForReg = new LocalEntry[regSize]; - /** indexed by register, the last local variable live in a reg */ - private final LocalEntry[] lastEntryForReg; + thisStringIdx = thisIdx; + } - /** method descriptor of method this debug info is for */ - private final Prototype desc; + /** + * An entry in the resulting postions table + */ + public static class PositionEntry { + /** bytecode address */ + public int address; - /** true if method is static */ - private final boolean isStatic; + /** line number */ + public int line; - /** - * register size, in register units, of the register space - * used by this method - */ - private final int regSize; - - /** current decoding state: line number */ - private int line = 1; - - /** current decoding state: bytecode address */ - private int address = 0; - - /** string index of the string "this" */ - private final int thisStringIdx; - - /** - * Constructs an instance. - * - * @param encoded encoded debug info - * @param regSize register size, in register units, of the register space - * used by this method - * @param isStatic true if method is static - * @param ref method descriptor of method this debug info is for - * @param file dex file this debug info will be stored in - */ - DebugInfoDecoder(byte[] encoded, int regSize, - boolean isStatic, CstMethodRef ref, DexFile file) { - this(new ByteArrayByteInput(encoded), regSize, isStatic, ref.getPrototype(), - extractThisIdx(file)); + public PositionEntry(int address, int line) { + this.address = address; + this.line = line; + } + } + + /** + * An entry in the resulting locals table + */ + public static class LocalEntry { + /** address of event */ + public int address; + + /** {@code true} iff it's a local start */ + public boolean isStart; + + /** register number */ + public int reg; + + /** index of name in strings table */ + public int nameIndex; + + /** index of type in types table */ + public int typeIndex; + + /** index of type signature in strings table */ + public int signatureIndex; + + public LocalEntry(int address, + boolean isStart, + int reg, + int nameIndex, + int typeIndex, + int signatureIndex) { + this.address = address; + this.isStart = isStart; + this.reg = reg; + this.nameIndex = nameIndex; + this.typeIndex = typeIndex; + this.signatureIndex = signatureIndex; } - /** - * Constructs an instance. - * - * @param encoded encoded debug info - * @param regSize register size, in register units, of the register space - * used by this method - * @param isStatic true if method is static - * @param desc method descriptor of method this debug info is for - * @param thisIdx string index of the string "this" or -1 if the string does not exists in the - * dex. - */ - public DebugInfoDecoder(ByteInput encoded, int regSize, - boolean isStatic, Prototype desc, int thisIdx) { - if (encoded == null) { - throw new NullPointerException("encoded == null"); - } + @Override + public String toString() { + return String.format("[%x %s v%d %04x %04x %04x]", + address, + isStart ? "start" : "end", + reg, + nameIndex, + typeIndex, + signatureIndex); + } + } + + /** + * Gets the decoded positions list. + * Valid after calling {@code decode}. + * + * @return positions list in ascending address order. + */ + public List<PositionEntry> getPositionList() { + return positions; + } + + /** + * Gets the decoded locals list, in ascending start-address order. + * Valid after calling {@code decode}. + * + * @return locals list in ascending address order. + */ + public List<LocalEntry> getLocals() { + return locals; + } + + /** + * Decodes the debug info sequence. + */ + public void decode() { + try { + decode0(); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, "...while decoding debug info"); + } + } + + /** + * Reads a string index. String indicies are offset by 1, and a 0 value + * in the stream (-1 as returned by this method) means "null" + * + * @return index into file's string ids table, -1 means null + * @throws IOException + */ + private int readStringIndex(ByteInput bs) throws IOException { + int offsetIndex = Leb128Utils.readUnsignedLeb128(bs); + + return offsetIndex - 1; + } + + /** + * Gets the register that begins the method's parameter range (including + * the 'this' parameter for non-static methods). The range continues until + * {@code regSize} + * + * @return register as noted above. + */ + private int getParamBase() { + return regSize - desc.getParameterTypes().getWordCount() - (isStatic ? 0 : 1); + } + + private void decode0() throws IOException { + line = Leb128Utils.readUnsignedLeb128(encoded); + int szParams = Leb128Utils.readUnsignedLeb128(encoded); + StdTypeList params = desc.getParameterTypes(); + int curReg = getParamBase(); + + if (szParams != params.size()) { + throw new RuntimeException("Mismatch between parameters_size and prototype"); + } + + if (!isStatic) { + // Start off with implicit 'this' entry + LocalEntry thisEntry = + new LocalEntry(0, true, curReg, thisStringIdx, ClassDef.NO_INDEX, ClassDef.NO_INDEX); + locals.add(thisEntry); + lastEntryForReg[curReg] = thisEntry; + curReg++; + } - this.encoded = encoded; - this.isStatic = isStatic; - this.desc = desc; - this.regSize = regSize; + for (int i = 0; i < szParams; i++) { + Type paramType = params.getType(i); + LocalEntry le; - positions = new ArrayList<PositionEntry>(); - locals = new ArrayList<LocalEntry>(); - lastEntryForReg = new LocalEntry[regSize]; + int nameIdx = readStringIndex(encoded); - thisStringIdx = thisIdx; + if (nameIdx == -1) { + /* + * Unnamed parameter; often but not always filled in by an + * extended start op after the prologue + */ + le = new LocalEntry(0, + true, + curReg, + ClassDef.NO_INDEX, + ClassDef.NO_INDEX, + ClassDef.NO_INDEX); + } else { + // TODO(dx team): Final 0 should be idx of paramType.getDescriptor(). + le = new LocalEntry(0, true, curReg, nameIdx, ClassDef.NO_INDEX, ClassDef.NO_INDEX); + } + + locals.add(le); + lastEntryForReg[curReg] = le; + curReg += paramType.getCategory(); } - /** - * An entry in the resulting postions table - */ - static public class PositionEntry { - /** bytecode address */ - public int address; + for (;;) { + int opcode = encoded.readByte() & 0xff; - /** line number */ - public int line; + switch (opcode) { + case DBG_START_LOCAL: { + int reg = Leb128Utils.readUnsignedLeb128(encoded); + int nameIdx = readStringIndex(encoded); + int typeIdx = readStringIndex(encoded); + LocalEntry le = new LocalEntry(address, true, reg, nameIdx, typeIdx, ClassDef.NO_INDEX); - public PositionEntry(int address, int line) { - this.address = address; - this.line = line; + locals.add(le); + lastEntryForReg[reg] = le; } - } - - /** - * An entry in the resulting locals table - */ - static public class LocalEntry { - /** address of event */ - public int address; + break; - /** {@code true} iff it's a local start */ - public boolean isStart; + case DBG_START_LOCAL_EXTENDED: { + int reg = Leb128Utils.readUnsignedLeb128(encoded); + int nameIdx = readStringIndex(encoded); + int typeIdx = readStringIndex(encoded); + int sigIdx = readStringIndex(encoded); + LocalEntry le = new LocalEntry(address, true, reg, nameIdx, typeIdx, sigIdx); - /** register number */ - public int reg; + locals.add(le); + lastEntryForReg[reg] = le; + } + break; - /** index of name in strings table */ - public int nameIndex; + case DBG_RESTART_LOCAL: { + int reg = Leb128Utils.readUnsignedLeb128(encoded); + LocalEntry prevle; + LocalEntry le; - /** index of type in types table */ - public int typeIndex; + try { + prevle = lastEntryForReg[reg]; - /** index of type signature in strings table */ - public int signatureIndex; + if (prevle.isStart) { + throw new RuntimeException("nonsensical " + "RESTART_LOCAL on live register v" + reg); + } - public LocalEntry(int address, boolean isStart, int reg, int nameIndex, - int typeIndex, int signatureIndex) { - this.address = address; - this.isStart = isStart; - this.reg = reg; - this.nameIndex = nameIndex; - this.typeIndex = typeIndex; - this.signatureIndex = signatureIndex; + le = new LocalEntry(address, + true, + reg, + prevle.nameIndex, + prevle.typeIndex, + prevle.signatureIndex); + } catch (NullPointerException ex) { + throw new RuntimeException("Encountered RESTART_LOCAL on new v" + reg); + } + + locals.add(le); + lastEntryForReg[reg] = le; } + break; - public String toString() { - return String.format("[%x %s v%d %04x %04x %04x]", - address, isStart ? "start" : "end", reg, - nameIndex, typeIndex, signatureIndex); - } - } + case DBG_END_LOCAL: { + int reg = Leb128Utils.readUnsignedLeb128(encoded); + LocalEntry prevle; + LocalEntry le; - /** - * Gets the decoded positions list. - * Valid after calling {@code decode}. - * - * @return positions list in ascending address order. - */ - public List<PositionEntry> getPositionList() { - return positions; - } + try { + prevle = lastEntryForReg[reg]; - /** - * Gets the decoded locals list, in ascending start-address order. - * Valid after calling {@code decode}. - * - * @return locals list in ascending address order. - */ - public List<LocalEntry> getLocals() { - return locals; - } + if (!prevle.isStart) { + throw new RuntimeException("nonsensical " + "END_LOCAL on dead register v" + reg); + } - /** - * Decodes the debug info sequence. - */ - public void decode() { - try { - decode0(); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, - "...while decoding debug info"); + le = new LocalEntry(address, + false, + reg, + prevle.nameIndex, + prevle.typeIndex, + prevle.signatureIndex); + } catch (NullPointerException ex) { + throw new RuntimeException("Encountered END_LOCAL on new v" + reg); + } + + locals.add(le); + lastEntryForReg[reg] = le; } - } + break; - /** - * Reads a string index. String indicies are offset by 1, and a 0 value - * in the stream (-1 as returned by this method) means "null" - * - * @return index into file's string ids table, -1 means null - * @throws IOException - */ - private int readStringIndex(ByteInput bs) throws IOException { - int offsetIndex = Leb128Utils.readUnsignedLeb128(bs); + case DBG_END_SEQUENCE: + // all done + return; - return offsetIndex - 1; - } + case DBG_ADVANCE_PC: + address += Leb128Utils.readUnsignedLeb128(encoded); + break; - /** - * Gets the register that begins the method's parameter range (including - * the 'this' parameter for non-static methods). The range continues until - * {@code regSize} - * - * @return register as noted above. - */ - private int getParamBase() { - return regSize - - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1); - } + case DBG_ADVANCE_LINE: + line += Leb128Utils.readSignedLeb128(encoded); + break; - private void decode0() throws IOException { - line = Leb128Utils.readUnsignedLeb128(encoded); - int szParams = Leb128Utils.readUnsignedLeb128(encoded); - StdTypeList params = desc.getParameterTypes(); - int curReg = getParamBase(); + case DBG_SET_PROLOGUE_END: + //TODO(dx team) do something with this. + break; - if (szParams != params.size()) { - throw new RuntimeException( - "Mismatch between parameters_size and prototype"); - } + case DBG_SET_EPILOGUE_BEGIN: + //TODO(dx team) do something with this. + break; - if (!isStatic) { - // Start off with implicit 'this' entry - LocalEntry thisEntry = - new LocalEntry(0, true, curReg, thisStringIdx, ClassDef.NO_INDEX, - ClassDef.NO_INDEX); - locals.add(thisEntry); - lastEntryForReg[curReg] = thisEntry; - curReg++; - } + case DBG_SET_FILE: + //TODO(dx team) do something with this. + break; - for (int i = 0; i < szParams; i++) { - Type paramType = params.getType(i); - LocalEntry le; - - int nameIdx = readStringIndex(encoded); - - if (nameIdx == -1) { - /* - * Unnamed parameter; often but not always filled in by an - * extended start op after the prologue - */ - le = new LocalEntry(0, true, curReg, ClassDef.NO_INDEX, ClassDef.NO_INDEX, - ClassDef.NO_INDEX); - } else { - // TODO: Final 0 should be idx of paramType.getDescriptor(). - le = new LocalEntry(0, true, curReg, nameIdx, ClassDef.NO_INDEX, ClassDef.NO_INDEX); - } + default: + if (opcode < DBG_FIRST_SPECIAL) { + throw new RuntimeException("Invalid extended opcode encountered " + opcode); + } - locals.add(le); - lastEntryForReg[curReg] = le; - curReg += paramType.getCategory(); - } + int adjopcode = opcode - DBG_FIRST_SPECIAL; - for (;;) { - int opcode = encoded.readByte() & 0xff; - - switch (opcode) { - case DBG_START_LOCAL: { - int reg = Leb128Utils.readUnsignedLeb128(encoded); - int nameIdx = readStringIndex(encoded); - int typeIdx = readStringIndex(encoded); - LocalEntry le = new LocalEntry( - address, true, reg, nameIdx, typeIdx, ClassDef.NO_INDEX); - - locals.add(le); - lastEntryForReg[reg] = le; - } - break; - - case DBG_START_LOCAL_EXTENDED: { - int reg = Leb128Utils.readUnsignedLeb128(encoded); - int nameIdx = readStringIndex(encoded); - int typeIdx = readStringIndex(encoded); - int sigIdx = readStringIndex(encoded); - LocalEntry le = new LocalEntry( - address, true, reg, nameIdx, typeIdx, sigIdx); - - locals.add(le); - lastEntryForReg[reg] = le; - } - break; - - case DBG_RESTART_LOCAL: { - int reg = Leb128Utils.readUnsignedLeb128(encoded); - LocalEntry prevle; - LocalEntry le; - - try { - prevle = lastEntryForReg[reg]; - - if (prevle.isStart) { - throw new RuntimeException("nonsensical " - + "RESTART_LOCAL on live register v" - + reg); - } - - le = new LocalEntry(address, true, reg, - prevle.nameIndex, prevle.typeIndex, prevle.signatureIndex); - } catch (NullPointerException ex) { - throw new RuntimeException( - "Encountered RESTART_LOCAL on new v" + reg); - } - - locals.add(le); - lastEntryForReg[reg] = le; - } - break; - - case DBG_END_LOCAL: { - int reg = Leb128Utils.readUnsignedLeb128(encoded); - LocalEntry prevle; - LocalEntry le; - - try { - prevle = lastEntryForReg[reg]; - - if (!prevle.isStart) { - throw new RuntimeException("nonsensical " - + "END_LOCAL on dead register v" + reg); - } - - le = new LocalEntry(address, false, reg, - prevle.nameIndex, prevle.typeIndex, - prevle.signatureIndex); - } catch (NullPointerException ex) { - throw new RuntimeException( - "Encountered END_LOCAL on new v" + reg); - } - - locals.add(le); - lastEntryForReg[reg] = le; - } - break; - - case DBG_END_SEQUENCE: - // all done - return; - - case DBG_ADVANCE_PC: - address += Leb128Utils.readUnsignedLeb128(encoded); - break; - - case DBG_ADVANCE_LINE: - line += Leb128Utils.readSignedLeb128(encoded); - break; - - case DBG_SET_PROLOGUE_END: - //TODO do something with this. - break; - - case DBG_SET_EPILOGUE_BEGIN: - //TODO do something with this. - break; - - case DBG_SET_FILE: - //TODO do something with this. - break; - - default: - if (opcode < DBG_FIRST_SPECIAL) { - throw new RuntimeException( - "Invalid extended opcode encountered " - + opcode); - } - - int adjopcode = opcode - DBG_FIRST_SPECIAL; - - address += adjopcode / DBG_LINE_RANGE; - line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE); - - positions.add(new PositionEntry(address, line)); - break; + address += adjopcode / DBG_LINE_RANGE; + line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE); - } - } - } + positions.add(new PositionEntry(address, line)); + break; - /** - * Validates an encoded debug info stream against data used to encode it, - * throwing an exception if they do not match. Used to validate the - * encoder. - * - * @param info encoded debug info - * @param file {@code non-null;} file to refer to during decoding - * @param ref {@code non-null;} method whose info is being decoded - * @param code {@code non-null;} original code object that was encoded - * @param isStatic whether the method is static - */ - public static void validateEncode(byte[] info, DexFile file, - CstMethodRef ref, DalvCode code, boolean isStatic) { - PositionList pl = code.getPositions(); - LocalList ll = code.getLocals(); - DalvInsnList insns = code.getInsns(); - int countRegisters = insns.getRegistersSize(); - - try { - validateEncode0(info, countRegisters, - isStatic, ref, file, pl, ll); - } catch (RuntimeException ex) { - System.err.println("instructions:"); - insns.debugPrint(System.err, " ", true); - System.err.println("local list:"); - ll.debugPrint(System.err, " "); - throw ExceptionWithContext.withContext(ex, - "while processing " + ref.toHuman()); - } + } } + } + + /** + * Validates an encoded debug info stream against data used to encode it, + * throwing an exception if they do not match. Used to validate the + * encoder. + * + * @param info encoded debug info + * @param file {@code non-null;} file to refer to during decoding + * @param ref {@code non-null;} method whose info is being decoded + * @param code {@code non-null;} original code object that was encoded + * @param isStatic whether the method is static + */ + public static void validateEncode(byte[] info, DexFile file, CstMethodRef ref, DalvCode code, + boolean isStatic) { + PositionList pl = code.getPositions(); + LocalList ll = code.getLocals(); + DalvInsnList insns = code.getInsns(); + int countRegisters = insns.getRegistersSize(); + + try { + validateEncode0(info, countRegisters, isStatic, ref, file, pl, ll); + } catch (RuntimeException ex) { + System.err.println("instructions:"); + insns.debugPrint(System.err, " ", true); + System.err.println("local list:"); + ll.debugPrint(System.err, " "); + throw ExceptionWithContext.withContext(ex, "while processing " + ref.toHuman()); + } + } + + private static void validateEncode0(byte[] info, + int countRegisters, + boolean isStatic, + CstMethodRef ref, + DexFile file, + PositionList pl, + LocalList ll) { + DebugInfoDecoder decoder = new DebugInfoDecoder(info, countRegisters, isStatic, ref, file); + + decoder.decode(); + + /* + * Go through the decoded position entries, matching up + * with original entries. + */ - private static void validateEncode0(byte[] info, - int countRegisters, boolean isStatic, CstMethodRef ref, - DexFile file, PositionList pl, LocalList ll) { - DebugInfoDecoder decoder - = new DebugInfoDecoder(info, countRegisters, - isStatic, ref, file); - - decoder.decode(); +List<PositionEntry> decodedEntries = decoder.getPositionList(); - /* - * Go through the decoded position entries, matching up - * with original entries. - */ + if (decodedEntries.size() != pl.size()) { + throw new RuntimeException("Decoded positions table not same size was " + + decodedEntries.size() + " expected " + pl.size()); + } - List<PositionEntry> decodedEntries = decoder.getPositionList(); + for (PositionEntry entry : decodedEntries) { + boolean found = false; + for (int i = pl.size() - 1; i >= 0; i--) { + PositionList.Entry ple = pl.get(i); - if (decodedEntries.size() != pl.size()) { - throw new RuntimeException( - "Decoded positions table not same size was " - + decodedEntries.size() + " expected " + pl.size()); + if (entry.line == ple.getPosition().getLine() && entry.address == ple.getAddress()) { + found = true; + break; } + } - for (PositionEntry entry : decodedEntries) { - boolean found = false; - for (int i = pl.size() - 1; i >= 0; i--) { - PositionList.Entry ple = pl.get(i); + if (!found) { + throw new RuntimeException( + "Could not match position entry: " + entry.address + ", " + entry.line); + } + } - if (entry.line == ple.getPosition().getLine() - && entry.address == ple.getAddress()) { - found = true; - break; - } - } + /* + * Go through the original local list, in order, matching up + * with decoded entries. + */ - if (!found) { - throw new RuntimeException ("Could not match position entry: " - + entry.address + ", " + entry.line); - } +List<LocalEntry> decodedLocals = decoder.getLocals(); + int thisStringIdx = decoder.thisStringIdx; + int decodedSz = decodedLocals.size(); + int paramBase = decoder.getParamBase(); + + /* + * Preflight to fill in any parameters that were skipped in + * the prologue (including an implied "this") but then + * identified by full signature. + */ + for (int i = 0; i < decodedSz; i++) { + LocalEntry entry = decodedLocals.get(i); + int idx = entry.nameIndex; + + if ((idx < 0) || (idx == thisStringIdx)) { + for (int j = i + 1; j < decodedSz; j++) { + LocalEntry e2 = decodedLocals.get(j); + if (e2.address != 0) { + break; + } + if ((entry.reg == e2.reg) && e2.isStart) { + decodedLocals.set(i, e2); + decodedLocals.remove(j); + decodedSz--; + break; + } } + } + } - /* - * Go through the original local list, in order, matching up - * with decoded entries. - */ + int origSz = ll.size(); + int decodeAt = 0; + boolean problem = false; - List<LocalEntry> decodedLocals = decoder.getLocals(); - int thisStringIdx = decoder.thisStringIdx; - int decodedSz = decodedLocals.size(); - int paramBase = decoder.getParamBase(); + for (int i = 0; i < origSz; i++) { + LocalList.Entry origEntry = ll.get(i); + if (origEntry.getDisposition() == LocalList.Disposition.END_REPLACED) { /* - * Preflight to fill in any parameters that were skipped in - * the prologue (including an implied "this") but then - * identified by full signature. + * The encoded list doesn't represent replacements, so + * ignore them for the sake of comparison. */ - for (int i = 0; i < decodedSz; i++) { - LocalEntry entry = decodedLocals.get(i); - int idx = entry.nameIndex; - - if ((idx < 0) || (idx == thisStringIdx)) { - for (int j = i + 1; j < decodedSz; j++) { - LocalEntry e2 = decodedLocals.get(j); - if (e2.address != 0) { - break; - } - if ((entry.reg == e2.reg) && e2.isStart) { - decodedLocals.set(i, e2); - decodedLocals.remove(j); - decodedSz--; - break; - } - } - } - } - - int origSz = ll.size(); - int decodeAt = 0; - boolean problem = false; - - for (int i = 0; i < origSz; i++) { - LocalList.Entry origEntry = ll.get(i); + continue; + } - if (origEntry.getDisposition() - == LocalList.Disposition.END_REPLACED) { - /* - * The encoded list doesn't represent replacements, so - * ignore them for the sake of comparison. - */ - continue; - } - - LocalEntry decodedEntry; - - do { - decodedEntry = decodedLocals.get(decodeAt); - if (decodedEntry.nameIndex >= 0) { - break; - } - /* - * A negative name index means this is an anonymous - * parameter, and we shouldn't expect to see it in the - * original list. So, skip it. - */ - decodeAt++; - } while (decodeAt < decodedSz); - - int decodedAddress = decodedEntry.address; - - if (decodedEntry.reg != origEntry.getRegister()) { - System.err.println("local register mismatch at orig " + i + - " / decoded " + decodeAt); - problem = true; - break; - } - - if (decodedEntry.isStart != origEntry.isStart()) { - System.err.println("local start/end mismatch at orig " + i + - " / decoded " + decodeAt); - problem = true; - break; - } + LocalEntry decodedEntry; - /* - * The secondary check here accounts for the fact that a - * parameter might not be marked as starting at 0 in the - * original list. - */ - if ((decodedAddress != origEntry.getAddress()) - && !((decodedAddress == 0) - && (decodedEntry.reg >= paramBase))) { - System.err.println("local address mismatch at orig " + i + - " / decoded " + decodeAt); - problem = true; - break; - } - - decodeAt++; - } - - if (problem) { - System.err.println("decoded locals:"); - for (LocalEntry e : decodedLocals) { - System.err.println(" " + e); - } - throw new RuntimeException("local table problem"); + do { + decodedEntry = decodedLocals.get(decodeAt); + if (decodedEntry.nameIndex >= 0) { + break; } + /* + * A negative name index means this is an anonymous + * parameter, and we shouldn't expect to see it in the + * original list. So, skip it. + */ + decodeAt++; + } while (decodeAt < decodedSz); + + int decodedAddress = decodedEntry.address; + + if (decodedEntry.reg != origEntry.getRegister()) { + System.err.println("local register mismatch at orig " + i + " / decoded " + decodeAt); + problem = true; + break; + } + + if (decodedEntry.isStart != origEntry.isStart()) { + System.err.println("local start/end mismatch at orig " + i + " / decoded " + decodeAt); + problem = true; + break; + } + + /* + * The secondary check here accounts for the fact that a + * parameter might not be marked as starting at 0 in the + * original list. + */ + if ((decodedAddress != origEntry.getAddress()) + && !((decodedAddress == 0) && (decodedEntry.reg >= paramBase))) { + System.err.println("local address mismatch at orig " + i + " / decoded " + decodeAt); + problem = true; + break; + } + + decodeAt++; } - private static int extractThisIdx(DexFile file) { - int idx = -1; - - try { - idx = file.getStringIds().indexOf(new CstString("this")); - } catch (IllegalArgumentException ex) { - /* - * Silently tolerate not finding "this". It just means that - * no method has local variable info that looks like - * a standard instance method. - */ - } - return idx; + if (problem) { + System.err.println("decoded locals:"); + for (LocalEntry e : decodedLocals) { + System.err.println(" " + e); + } + throw new RuntimeException("local table problem"); + } + } + + private static int extractThisIdx(DexFile file) { + int idx = -1; + + try { + idx = file.getStringIds().indexOf(new CstString("this")); + } catch (IllegalArgumentException ex) { + /* + * Silently tolerate not finding "this". It just means that + * no method has local variable info that looks like + * a standard instance method. + */ } + return idx; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/DebugInfoEncoder.java b/dx/src/com/android/jack/dx/dex/file/DebugInfoEncoder.java index 026d0c0..725200d 100644 --- a/dx/src/com/android/jack/dx/dex/file/DebugInfoEncoder.java +++ b/dx/src/com/android/jack/dx/dex/file/DebugInfoEncoder.java @@ -16,6 +16,18 @@ package com.android.jack.dx.dex.file; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_ADVANCE_LINE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_ADVANCE_PC; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_END_LOCAL; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_END_SEQUENCE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_FIRST_SPECIAL; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_LINE_BASE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_LINE_RANGE; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_RESTART_LOCAL; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_SET_PROLOGUE_END; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_START_LOCAL; +import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_START_LOCAL_EXTENDED; + import com.android.jack.dx.dex.code.LocalList; import com.android.jack.dx.dex.code.PositionList; import com.android.jack.dx.rop.code.RegisterSpec; @@ -33,11 +45,9 @@ import com.android.jack.dx.util.ExceptionWithContext; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.BitSet; import java.util.Collections; import java.util.Comparator; -import java.util.BitSet; - -import static com.android.jack.dx.dex.file.DebugInfoConstants.*; /** * An encoder for the dex debug info state machine format. The format @@ -53,868 +63,842 @@ import static com.android.jack.dx.dex.file.DebugInfoConstants.*; * </ol> */ public final class DebugInfoEncoder { - private static final boolean DEBUG = false; - - /** {@code null-ok;} positions (line numbers) to encode */ - private final PositionList positions; - - /** {@code null-ok;} local variables to encode */ - private final LocalList locals; - - private final ByteArrayAnnotatedOutput output; - private final DexFile file; - private final int codeSize; - private final int regSize; - - private final Prototype desc; - private final boolean isStatic; - - /** current encoding state: bytecode address */ - private int address = 0; - - /** current encoding state: line number */ - private int line = 1; - - /** - * if non-null: the output to write annotations to. No normal - * output is written to this. - */ - private AnnotatedOutput annotateTo; - - /** if non-null: another possible output for annotations */ - private PrintWriter debugPrint; - - /** if non-null: the prefix for each annotation or debugPrint line */ - private String prefix; - - /** true if output should be consumed during annotation */ - private boolean shouldConsume; - - /** indexed by register; last local alive in register */ - private final LocalList.Entry[] lastEntryForReg; - - /** - * Creates an instance. - * - * @param positions {@code null-ok;} positions (line numbers) to encode - * @param locals {@code null-ok;} local variables to encode - * @param file {@code null-ok;} may only be {@code null} if simply using - * this class to do a debug print - * @param codeSize - * @param regSize - * @param isStatic - * @param ref - */ - public DebugInfoEncoder(PositionList positions, LocalList locals, - DexFile file, int codeSize, int regSize, - boolean isStatic, CstMethodRef ref) { - this.positions = positions; - this.locals = locals; - this.file = file; - this.desc = ref.getPrototype(); - this.isStatic = isStatic; - this.codeSize = codeSize; - this.regSize = regSize; - - output = new ByteArrayAnnotatedOutput(); - lastEntryForReg = new LocalList.Entry[regSize]; - } - - /** - * Annotates or writes a message to the {@code debugPrint} writer - * if applicable. - * - * @param length the number of bytes associated with this message - * @param message the message itself - */ - private void annotate(int length, String message) { - if (prefix != null) { - message = prefix + message; - } - - if (annotateTo != null) { - annotateTo.annotate(shouldConsume ? length : 0, message); - } - - if (debugPrint != null) { - debugPrint.println(message); - } + private static final boolean DEBUG = false; + + /** {@code null-ok;} positions (line numbers) to encode */ + private final PositionList positions; + + /** {@code null-ok;} local variables to encode */ + private final LocalList locals; + + private final ByteArrayAnnotatedOutput output; + private final DexFile file; + private final int codeSize; + private final int regSize; + + private final Prototype desc; + private final boolean isStatic; + + /** current encoding state: bytecode address */ + private int address = 0; + + /** current encoding state: line number */ + private int line = 1; + + /** + * if non-null: the output to write annotations to. No normal + * output is written to this. + */ + private AnnotatedOutput annotateTo; + + /** if non-null: another possible output for annotations */ + private PrintWriter debugPrint; + + /** if non-null: the prefix for each annotation or debugPrint line */ + private String prefix; + + /** true if output should be consumed during annotation */ + private boolean shouldConsume; + + /** indexed by register; last local alive in register */ + private final LocalList.Entry[] lastEntryForReg; + + /** + * Creates an instance. + * + * @param positions {@code null-ok;} positions (line numbers) to encode + * @param locals {@code null-ok;} local variables to encode + * @param file {@code null-ok;} may only be {@code null} if simply using + * this class to do a debug print + * @param codeSize + * @param regSize + * @param isStatic + * @param ref + */ + public DebugInfoEncoder(PositionList positions, + LocalList locals, + DexFile file, + int codeSize, + int regSize, + boolean isStatic, + CstMethodRef ref) { + this.positions = positions; + this.locals = locals; + this.file = file; + this.desc = ref.getPrototype(); + this.isStatic = isStatic; + this.codeSize = codeSize; + this.regSize = regSize; + + output = new ByteArrayAnnotatedOutput(); + lastEntryForReg = new LocalList.Entry[regSize]; + } + + /** + * Annotates or writes a message to the {@code debugPrint} writer + * if applicable. + * + * @param length the number of bytes associated with this message + * @param message the message itself + */ + private void annotate(int length, String message) { + if (prefix != null) { + message = prefix + message; } - /** - * Converts this (PositionList, LocalList) pair into a state machine - * sequence. - * - * @return {@code non-null;} encoded byte sequence without padding and - * terminated with a {@code 0x00} byte - */ - public byte[] convert() { - try { - byte[] ret; - ret = convert0(); - - if (DEBUG) { - for (int i = 0 ; i < ret.length; i++) { - System.err.printf("byte %02x\n", (0xff & ret[i])); - } - } - - return ret; - } catch (IOException ex) { - throw ExceptionWithContext - .withContext(ex, "...while encoding debug info"); - } + if (annotateTo != null) { + annotateTo.annotate(shouldConsume ? length : 0, message); } - /** - * Converts and produces annotations on a stream. Does not write - * actual bits to the {@code AnnotatedOutput}. - * - * @param prefix {@code null-ok;} prefix to attach to each line of output - * @param debugPrint {@code null-ok;} if specified, an alternate output for - * annotations - * @param out {@code null-ok;} if specified, where annotations should go - * @param consume whether to claim to have consumed output for - * {@code out} - * @return {@code non-null;} encoded output - */ - public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint, - AnnotatedOutput out, boolean consume) { - this.prefix = prefix; - this.debugPrint = debugPrint; - annotateTo = out; - shouldConsume = consume; - - byte[] result = convert(); - - return result; + if (debugPrint != null) { + debugPrint.println(message); } - - private byte[] convert0() throws IOException { - ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions(); - ArrayList<LocalList.Entry> methodArgs = extractMethodArguments(); - - emitHeader(sortedPositions, methodArgs); - - // TODO: Make this mark be the actual prologue end. - output.writeByte(DBG_SET_PROLOGUE_END); - - if (annotateTo != null || debugPrint != null) { - annotate(1, String.format("%04x: prologue end",address)); - } - - int positionsSz = sortedPositions.size(); - int localsSz = locals.size(); - - // Current index in sortedPositions - int curPositionIdx = 0; - // Current index in locals - int curLocalIdx = 0; - - for (;;) { - /* - * Emit any information for the current address. - */ - - curLocalIdx = emitLocalsAtAddress(curLocalIdx); - curPositionIdx = - emitPositionsAtAddress(curPositionIdx, sortedPositions); - - /* - * Figure out what the next important address is. - */ - - int nextAddrL = Integer.MAX_VALUE; // local variable - int nextAddrP = Integer.MAX_VALUE; // position (line number) - - if (curLocalIdx < localsSz) { - nextAddrL = locals.get(curLocalIdx).getAddress(); - } - - if (curPositionIdx < positionsSz) { - nextAddrP = sortedPositions.get(curPositionIdx).getAddress(); - } - - int next = Math.min(nextAddrP, nextAddrL); - - // No next important address == done. - if (next == Integer.MAX_VALUE) { - break; - } - - /* - * If the only work remaining are local ends at the end of the - * block, stop here. Those are implied anyway. - */ - if (next == codeSize - && nextAddrL == Integer.MAX_VALUE - && nextAddrP == Integer.MAX_VALUE) { - break; - } - - if (next == nextAddrP) { - // Combined advance PC + position entry - emitPosition(sortedPositions.get(curPositionIdx++)); - } else { - emitAdvancePc(next - address); - } - } - - emitEndSequence(); - - return output.toByteArray(); + } + + /** + * Converts this (PositionList, LocalList) pair into a state machine + * sequence. + * + * @return {@code non-null;} encoded byte sequence without padding and + * terminated with a {@code 0x00} byte + */ + public byte[] convert() { + try { + byte[] ret; + ret = convert0(); + + if (DEBUG) { + for (int i = 0; i < ret.length; i++) { + System.err.printf("byte %02x\n", (0xff & ret[i])); + } + } + + return ret; + } catch (IOException ex) { + throw ExceptionWithContext.withContext(ex, "...while encoding debug info"); } - - /** - * Emits all local variable activity that occurs at the current - * {@link #address} starting at the given index into {@code - * locals} and including all subsequent activity at the same - * address. - * - * @param curLocalIdx Current index in locals - * @return new value for {@code curLocalIdx} - * @throws IOException - */ - private int emitLocalsAtAddress(int curLocalIdx) - throws IOException { - int sz = locals.size(); - - // TODO: Don't emit ends implied by starts. - - while ((curLocalIdx < sz) - && (locals.get(curLocalIdx).getAddress() == address)) { - LocalList.Entry entry = locals.get(curLocalIdx++); - int reg = entry.getRegister(); - LocalList.Entry prevEntry = lastEntryForReg[reg]; - - if (entry == prevEntry) { - /* - * Here we ignore locals entries for parameters, - * which have already been represented and placed in the - * lastEntryForReg array. - */ - continue; - } - - // At this point we have a new entry one way or another. - lastEntryForReg[reg] = entry; - - if (entry.isStart()) { - if ((prevEntry != null) && entry.matches(prevEntry)) { - /* - * The previous local in this register has the same - * name and type as the one being introduced now, so - * use the more efficient "restart" form. - */ - if (prevEntry.isStart()) { - /* - * We should never be handed a start when a - * a matching local is already active. - */ - throw new RuntimeException("shouldn't happen"); - } - emitLocalRestart(entry); - } else { - emitLocalStart(entry); - } - } else { - /* - * Only emit a local end if it is *not* due to a direct - * replacement. Direct replacements imply an end of the - * previous local in the same register. - * - * TODO: Make sure the runtime can deal with implied - * local ends from category-2 interactions, and when so, - * also stop emitting local ends for those cases. - */ - if (entry.getDisposition() - != LocalList.Disposition.END_REPLACED) { - emitLocalEnd(entry); - } - } - } - - return curLocalIdx; + } + + /** + * Converts and produces annotations on a stream. Does not write + * actual bits to the {@code AnnotatedOutput}. + * + * @param prefix {@code null-ok;} prefix to attach to each line of output + * @param debugPrint {@code null-ok;} if specified, an alternate output for + * annotations + * @param out {@code null-ok;} if specified, where annotations should go + * @param consume whether to claim to have consumed output for + * {@code out} + * @return {@code non-null;} encoded output + */ + public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint, AnnotatedOutput out, + boolean consume) { + this.prefix = prefix; + this.debugPrint = debugPrint; + annotateTo = out; + shouldConsume = consume; + + byte[] result = convert(); + + return result; + } + + private byte[] convert0() throws IOException { + ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions(); + ArrayList<LocalList.Entry> methodArgs = extractMethodArguments(); + + emitHeader(sortedPositions, methodArgs); + + // TODO(dx team): Make this mark be the actual prologue end. + output.writeByte(DBG_SET_PROLOGUE_END); + + if (annotateTo != null || debugPrint != null) { + annotate(1, String.format("%04x: prologue end", address)); } - /** - * Emits all positions that occur at the current {@code address} - * - * @param curPositionIdx Current index in sortedPositions - * @param sortedPositions positions, sorted by ascending address - * @return new value for {@code curPositionIdx} - * @throws IOException - */ - private int emitPositionsAtAddress(int curPositionIdx, - ArrayList<PositionList.Entry> sortedPositions) - throws IOException { - int positionsSz = sortedPositions.size(); - while ((curPositionIdx < positionsSz) - && (sortedPositions.get(curPositionIdx).getAddress() - == address)) { - emitPosition(sortedPositions.get(curPositionIdx++)); - } - return curPositionIdx; + int positionsSz = sortedPositions.size(); + int localsSz = locals.size(); + + // Current index in sortedPositions + int curPositionIdx = 0; + // Current index in locals + int curLocalIdx = 0; + + for (;;) { + /* + * Emit any information for the current address. + */ + +curLocalIdx = emitLocalsAtAddress(curLocalIdx); + curPositionIdx = emitPositionsAtAddress(curPositionIdx, sortedPositions); + + /* + * Figure out what the next important address is. + */ + +int nextAddrL = Integer.MAX_VALUE; // local variable + int nextAddrP = Integer.MAX_VALUE; // position (line number) + + if (curLocalIdx < localsSz) { + nextAddrL = locals.get(curLocalIdx).getAddress(); + } + + if (curPositionIdx < positionsSz) { + nextAddrP = sortedPositions.get(curPositionIdx).getAddress(); + } + + int next = Math.min(nextAddrP, nextAddrL); + + // No next important address == done. + if (next == Integer.MAX_VALUE) { + break; + } + + /* + * If the only work remaining are local ends at the end of the + * block, stop here. Those are implied anyway. + */ + if (next == codeSize && nextAddrL == Integer.MAX_VALUE && nextAddrP == Integer.MAX_VALUE) { + break; + } + + if (next == nextAddrP) { + // Combined advance PC + position entry + emitPosition(sortedPositions.get(curPositionIdx++)); + } else { + emitAdvancePc(next - address); + } } - /** - * Emits the header sequence, which consists of LEB128-encoded initial - * line number and string indicies for names of all non-"this" arguments. - * - * @param sortedPositions positions, sorted by ascending address - * @param methodArgs local list entries for method argumens arguments, - * in left-to-right order omitting "this" - * @throws IOException - */ - private void emitHeader(ArrayList<PositionList.Entry> sortedPositions, - ArrayList<LocalList.Entry> methodArgs) throws IOException { - boolean annotate = (annotateTo != null) || (debugPrint != null); - int mark = output.getCursor(); - - // Start by initializing the line number register. - if (sortedPositions.size() > 0) { - PositionList.Entry entry = sortedPositions.get(0); - line = entry.getPosition().getLine(); - } - output.writeUleb128(line); + emitEndSequence(); - if (annotate) { - annotate(output.getCursor() - mark, "line_start: " + line); - } + return output.toByteArray(); + } - int curParam = getParamBase(); - // paramTypes will not include 'this' - StdTypeList paramTypes = desc.getParameterTypes(); - int szParamTypes = paramTypes.size(); + /** + * Emits all local variable activity that occurs at the current + * {@link #address} starting at the given index into {@code + * locals} and including all subsequent activity at the same + * address. + * + * @param curLocalIdx Current index in locals + * @return new value for {@code curLocalIdx} + * @throws IOException + */ + private int emitLocalsAtAddress(int curLocalIdx) throws IOException { + int sz = locals.size(); - /* - * Initialize lastEntryForReg to have an initial - * entry for the 'this' pointer. - */ - if (!isStatic) { - for (LocalList.Entry arg : methodArgs) { - if (curParam == arg.getRegister()) { - lastEntryForReg[curParam] = arg; - break; - } - } - curParam++; - } + // TODO(dx team): Don't emit ends implied by starts. - // Write out the number of parameter entries that will follow. - mark = output.getCursor(); - output.writeUleb128(szParamTypes); - - if (annotate) { - annotate(output.getCursor() - mark, - String.format("parameters_size: %04x", szParamTypes)); - } + while ((curLocalIdx < sz) && (locals.get(curLocalIdx).getAddress() == address)) { + LocalList.Entry entry = locals.get(curLocalIdx++); + int reg = entry.getRegister(); + LocalList.Entry prevEntry = lastEntryForReg[reg]; + if (entry == prevEntry) { /* - * Then emit the string indicies of all the method parameters. - * Note that 'this', if applicable, is excluded. + * Here we ignore locals entries for parameters, + * which have already been represented and placed in the + * lastEntryForReg array. */ - for (int i = 0; i < szParamTypes; i++) { - Type pt = paramTypes.get(i); - LocalList.Entry found = null; - - mark = output.getCursor(); - - for (LocalList.Entry arg : methodArgs) { - if (curParam == arg.getRegister()) { - found = arg; - - if (arg.getSignature() != null) { - /* - * Parameters with signatures will be re-emitted - * in complete as LOCAL_START_EXTENDED's below. - */ - emitStringIndex(null); - } else { - emitStringIndex(arg.getName()); - } - lastEntryForReg[curParam] = arg; - - break; - } - } - - if (found == null) { - /* - * Emit a null symbol for "unnamed." This is common - * for, e.g., synthesized methods and inner-class - * this$0 arguments. - */ - emitStringIndex(null); - } - - if (annotate) { - String parameterName - = (found == null || found.getSignature() != null) - ? "<unnamed>" : found.getName().toHuman(); - annotate(output.getCursor() - mark, - "parameter " + parameterName + " " - + RegisterSpec.PREFIX + curParam); - } - - curParam += pt.getCategory(); + continue; + } + + // At this point we have a new entry one way or another. + lastEntryForReg[reg] = entry; + + if (entry.isStart()) { + if ((prevEntry != null) && entry.matches(prevEntry)) { + /* + * The previous local in this register has the same + * name and type as the one being introduced now, so + * use the more efficient "restart" form. + */ + if (prevEntry.isStart()) { + /* + * We should never be handed a start when a + * a matching local is already active. + */ + throw new RuntimeException("shouldn't happen"); + } + emitLocalRestart(entry); + } else { + emitLocalStart(entry); } - + } else { /* - * If anything emitted above has a type signature, emit it again as - * a LOCAL_RESTART_EXTENDED + * Only emit a local end if it is *not* due to a direct + * replacement. Direct replacements imply an end of the + * previous local in the same register. + * + * TODO(dx team): Make sure the runtime can deal with implied + * local ends from category-2 interactions, and when so, + * also stop emitting local ends for those cases. */ - - for (LocalList.Entry arg : lastEntryForReg) { - if (arg == null) { - continue; - } - - CstString signature = arg.getSignature(); - - if (signature != null) { - emitLocalStartExtended(arg); - } + if (entry.getDisposition() != LocalList.Disposition.END_REPLACED) { + emitLocalEnd(entry); } + } } - /** - * Builds a list of position entries, sorted by ascending address. - * - * @return A sorted positions list - */ - private ArrayList<PositionList.Entry> buildSortedPositions() { - int sz = (positions == null) ? 0 : positions.size(); - ArrayList<PositionList.Entry> result = new ArrayList(sz); + return curLocalIdx; + } + + /** + * Emits all positions that occur at the current {@code address} + * + * @param curPositionIdx Current index in sortedPositions + * @param sortedPositions positions, sorted by ascending address + * @return new value for {@code curPositionIdx} + * @throws IOException + */ + private int emitPositionsAtAddress(int curPositionIdx, + ArrayList<PositionList.Entry> sortedPositions) throws IOException { + int positionsSz = sortedPositions.size(); + while ((curPositionIdx < positionsSz) + && (sortedPositions.get(curPositionIdx).getAddress() == address)) { + emitPosition(sortedPositions.get(curPositionIdx++)); + } + return curPositionIdx; + } + + /** + * Emits the header sequence, which consists of LEB128-encoded initial + * line number and string indicies for names of all non-"this" arguments. + * + * @param sortedPositions positions, sorted by ascending address + * @param methodArgs local list entries for method argumens arguments, + * in left-to-right order omitting "this" + * @throws IOException + */ + private void emitHeader(ArrayList<PositionList.Entry> sortedPositions, + ArrayList<LocalList.Entry> methodArgs) throws IOException { + boolean annotate = (annotateTo != null) || (debugPrint != null); + int mark = output.getCursor(); + + // Start by initializing the line number register. + if (sortedPositions.size() > 0) { + PositionList.Entry entry = sortedPositions.get(0); + line = entry.getPosition().getLine(); + } + output.writeUleb128(line); - for (int i = 0; i < sz; i++) { - result.add(positions.get(i)); - } + if (annotate) { + annotate(output.getCursor() - mark, "line_start: " + line); + } - // Sort ascending by address. - Collections.sort (result, new Comparator<PositionList.Entry>() { - public int compare (PositionList.Entry a, PositionList.Entry b) { - return a.getAddress() - b.getAddress(); - } - - public boolean equals (Object obj) { - return obj == this; - } - }); - return result; - } - - /** - * Gets the register that begins the method's parameter range (including - * the 'this' parameter for non-static methods). The range continues until - * {@code regSize} - * - * @return register as noted above - */ - private int getParamBase() { - return regSize - - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1); - } - - /** - * Extracts method arguments from a locals list. These will be collected - * from the input list and sorted by ascending register in the - * returned list. - * - * @return list of non-{@code this} method argument locals, - * sorted by ascending register - */ - private ArrayList<LocalList.Entry> extractMethodArguments() { - ArrayList<LocalList.Entry> result - = new ArrayList(desc.getParameterTypes().size()); - int argBase = getParamBase(); - BitSet seen = new BitSet(regSize - argBase); - int sz = locals.size(); - - for (int i = 0; i < sz; i++) { - LocalList.Entry e = locals.get(i); - int reg = e.getRegister(); - - if (reg < argBase) { - continue; - } - - // only the lowest-start-address entry is included. - if (seen.get(reg - argBase)) { - continue; - } - - seen.set(reg - argBase); - result.add(e); - } + int curParam = getParamBase(); + // paramTypes will not include 'this' + StdTypeList paramTypes = desc.getParameterTypes(); + int szParamTypes = paramTypes.size(); - // Sort by ascending register. - Collections.sort(result, new Comparator<LocalList.Entry>() { - public int compare(LocalList.Entry a, LocalList.Entry b) { - return a.getRegister() - b.getRegister(); - } + /* + * Initialize lastEntryForReg to have an initial + * entry for the 'this' pointer. + */ + if (!isStatic) { + for (LocalList.Entry arg : methodArgs) { + if (curParam == arg.getRegister()) { + lastEntryForReg[curParam] = arg; + break; + } + } + curParam++; + } - public boolean equals(Object obj) { - return obj == this; - } - }); + // Write out the number of parameter entries that will follow. + mark = output.getCursor(); + output.writeUleb128(szParamTypes); - return result; + if (annotate) { + annotate(output.getCursor() - mark, String.format("parameters_size: %04x", szParamTypes)); } - /** - * Returns a string representation of this LocalList entry that is - * appropriate for emitting as an annotation. - * - * @param e {@code non-null;} entry - * @return {@code non-null;} annotation string + /* + * Then emit the string indicies of all the method parameters. + * Note that 'this', if applicable, is excluded. */ - private String entryAnnotationString(LocalList.Entry e) { - StringBuilder sb = new StringBuilder(); + for (int i = 0; i < szParamTypes; i++) { + Type pt = paramTypes.get(i); + LocalList.Entry found = null; - sb.append(RegisterSpec.PREFIX); - sb.append(e.getRegister()); - sb.append(' '); + mark = output.getCursor(); - CstString name = e.getName(); - if (name == null) { - sb.append("null"); - } else { - sb.append(name.toHuman()); - } - sb.append(' '); + for (LocalList.Entry arg : methodArgs) { + if (curParam == arg.getRegister()) { + found = arg; - CstType type = e.getType(); - if (type == null) { - sb.append("null"); - } else { - sb.append(type.toHuman()); + if (arg.getSignature() != null) { + /* + * Parameters with signatures will be re-emitted + * in complete as LOCAL_START_EXTENDED's below. + */ + emitStringIndex(null); + } else { + emitStringIndex(arg.getName()); + } + lastEntryForReg[curParam] = arg; + + break; } + } - CstString signature = e.getSignature(); + if (found == null) { + /* + * Emit a null symbol for "unnamed." This is common + * for, e.g., synthesized methods and inner-class + * this$0 arguments. + */ + emitStringIndex(null); + } - if (signature != null) { - sb.append(' '); - sb.append(signature.toHuman()); - } + if (annotate) { + String parameterName = (found == null || found.getSignature() != null) ? "<unnamed>" + : found.getName().toHuman(); + annotate(output.getCursor() - mark, + "parameter " + parameterName + " " + RegisterSpec.PREFIX + curParam); + } - return sb.toString(); + curParam += pt.getCategory(); } - /** - * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL} - * sequence. - * - * @param entry entry associated with this restart - * @throws IOException + /* + * If anything emitted above has a type signature, emit it again as + * a LOCAL_RESTART_EXTENDED */ - private void emitLocalRestart(LocalList.Entry entry) - throws IOException { - int mark = output.getCursor(); +for (LocalList.Entry arg : lastEntryForReg) { + if (arg == null) { + continue; + } - output.writeByte(DBG_RESTART_LOCAL); - emitUnsignedLeb128(entry.getRegister()); + CstString signature = arg.getSignature(); - if (annotateTo != null || debugPrint != null) { - annotate(output.getCursor() - mark, - String.format("%04x: +local restart %s", - address, entryAnnotationString(entry))); - } + if (signature != null) { + emitLocalStartExtended(arg); + } + } + } + + /** + * Builds a list of position entries, sorted by ascending address. + * + * @return A sorted positions list + */ + private ArrayList<PositionList.Entry> buildSortedPositions() { + int sz = (positions == null) ? 0 : positions.size(); + ArrayList<PositionList.Entry> result = new ArrayList<PositionList.Entry>(sz); + + for (int i = 0; i < sz; i++) { + result.add(positions.get(i)); + } - if (DEBUG) { - System.err.println("emit local restart"); - } + // Sort ascending by address. + Collections.sort(result, new Comparator<PositionList.Entry>() { + @Override + public int compare(PositionList.Entry a, PositionList.Entry b) { + return a.getAddress() - b.getAddress(); + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + }); + return result; + } + + /** + * Gets the register that begins the method's parameter range (including + * the 'this' parameter for non-static methods). The range continues until + * {@code regSize} + * + * @return register as noted above + */ + private int getParamBase() { + return regSize - desc.getParameterTypes().getWordCount() - (isStatic ? 0 : 1); + } + + /** + * Extracts method arguments from a locals list. These will be collected + * from the input list and sorted by ascending register in the + * returned list. + * + * @return list of non-{@code this} method argument locals, + * sorted by ascending register + */ + private ArrayList<LocalList.Entry> extractMethodArguments() { + ArrayList<LocalList.Entry> result = + new ArrayList<LocalList.Entry>(desc.getParameterTypes().size()); + int argBase = getParamBase(); + BitSet seen = new BitSet(regSize - argBase); + int sz = locals.size(); + + for (int i = 0; i < sz; i++) { + LocalList.Entry e = locals.get(i); + int reg = e.getRegister(); + + if (reg < argBase) { + continue; + } + + // only the lowest-start-address entry is included. + if (seen.get(reg - argBase)) { + continue; + } + + seen.set(reg - argBase); + result.add(e); } - /** - * Emits a string index as an unsigned LEB128. The actual value written - * is shifted by 1, so that the '0' value is reserved for "null". The - * null symbol is used in some cases by the parameter name list - * at the beginning of the sequence. - * - * @param string {@code null-ok;} string to emit - * @throws IOException - */ - private void emitStringIndex(CstString string) throws IOException { - if ((string == null) || (file == null)) { - output.writeUleb128(0); - } else { - output.writeUleb128( - 1 + file.getStringIds().indexOf(string)); - } + // Sort by ascending register. + Collections.sort(result, new Comparator<LocalList.Entry>() { + @Override + public int compare(LocalList.Entry a, LocalList.Entry b) { + return a.getRegister() - b.getRegister(); + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + }); + + return result; + } + + /** + * Returns a string representation of this LocalList entry that is + * appropriate for emitting as an annotation. + * + * @param e {@code non-null;} entry + * @return {@code non-null;} annotation string + */ + private String entryAnnotationString(LocalList.Entry e) { + StringBuilder sb = new StringBuilder(); + + sb.append(RegisterSpec.PREFIX); + sb.append(e.getRegister()); + sb.append(' '); + + CstString name = e.getName(); + if (name == null) { + sb.append("null"); + } else { + sb.append(name.toHuman()); + } + sb.append(' '); - if (DEBUG) { - System.err.printf("Emit string %s\n", - string == null ? "<null>" : string.toQuoted()); - } + CstType type = e.getType(); + if (type == null) { + sb.append("null"); + } else { + sb.append(type.toHuman()); } - /** - * Emits a type index as an unsigned LEB128. The actual value written - * is shifted by 1, so that the '0' value is reserved for "null". - * - * @param type {@code null-ok;} type to emit - * @throws IOException - */ - private void emitTypeIndex(CstType type) throws IOException { - if ((type == null) || (file == null)) { - output.writeUleb128(0); - } else { - output.writeUleb128( - 1 + file.getTypeIds().indexOf(type)); - } + CstString signature = e.getSignature(); - if (DEBUG) { - System.err.printf("Emit type %s\n", - type == null ? "<null>" : type.toHuman()); - } + if (signature != null) { + sb.append(' '); + sb.append(signature.toHuman()); } - /** - * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or - * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED - * DBG_START_LOCAL_EXTENDED} sequence. - * - * @param entry entry to emit - * @throws IOException - */ - private void emitLocalStart(LocalList.Entry entry) - throws IOException { + return sb.toString(); + } - if (entry.getSignature() != null) { - emitLocalStartExtended(entry); - return; - } + /** + * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL} + * sequence. + * + * @param entry entry associated with this restart + * @throws IOException + */ + private void emitLocalRestart(LocalList.Entry entry) throws IOException { - int mark = output.getCursor(); + int mark = output.getCursor(); - output.writeByte(DBG_START_LOCAL); + output.writeByte(DBG_RESTART_LOCAL); + emitUnsignedLeb128(entry.getRegister()); - emitUnsignedLeb128(entry.getRegister()); - emitStringIndex(entry.getName()); - emitTypeIndex(entry.getType()); + if (annotateTo != null || debugPrint != null) { + annotate(output.getCursor() - mark, + String.format("%04x: +local restart %s", address, entryAnnotationString(entry))); + } - if (annotateTo != null || debugPrint != null) { - annotate(output.getCursor() - mark, - String.format("%04x: +local %s", address, - entryAnnotationString(entry))); - } + if (DEBUG) { + System.err.println("emit local restart"); + } + } + + /** + * Emits a string index as an unsigned LEB128. The actual value written + * is shifted by 1, so that the '0' value is reserved for "null". The + * null symbol is used in some cases by the parameter name list + * at the beginning of the sequence. + * + * @param string {@code null-ok;} string to emit + * @throws IOException + */ + private void emitStringIndex(CstString string) throws IOException { + if ((string == null) || (file == null)) { + output.writeUleb128(0); + } else { + output.writeUleb128(1 + file.getStringIds().indexOf(string)); + } - if (DEBUG) { - System.err.println("emit local start"); - } + if (DEBUG) { + System.err.printf("Emit string %s\n", string == null ? "<null>" : string.toQuoted()); + } + } + + /** + * Emits a type index as an unsigned LEB128. The actual value written + * is shifted by 1, so that the '0' value is reserved for "null". + * + * @param type {@code null-ok;} type to emit + * @throws IOException + */ + private void emitTypeIndex(CstType type) throws IOException { + if ((type == null) || (file == null)) { + output.writeUleb128(0); + } else { + output.writeUleb128(1 + file.getTypeIds().indexOf(type)); } - /** - * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED - * DBG_START_LOCAL_EXTENDED} sequence. - * - * @param entry entry to emit - * @throws IOException - */ - private void emitLocalStartExtended(LocalList.Entry entry) - throws IOException { + if (DEBUG) { + System.err.printf("Emit type %s\n", type == null ? "<null>" : type.toHuman()); + } + } + + /** + * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or + * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED + * DBG_START_LOCAL_EXTENDED} sequence. + * + * @param entry entry to emit + * @throws IOException + */ + private void emitLocalStart(LocalList.Entry entry) throws IOException { + + if (entry.getSignature() != null) { + emitLocalStartExtended(entry); + return; + } - int mark = output.getCursor(); + int mark = output.getCursor(); - output.writeByte(DBG_START_LOCAL_EXTENDED); + output.writeByte(DBG_START_LOCAL); - emitUnsignedLeb128(entry.getRegister()); - emitStringIndex(entry.getName()); - emitTypeIndex(entry.getType()); - emitStringIndex(entry.getSignature()); + emitUnsignedLeb128(entry.getRegister()); + emitStringIndex(entry.getName()); + emitTypeIndex(entry.getType()); - if (annotateTo != null || debugPrint != null) { - annotate(output.getCursor() - mark, - String.format("%04x: +localx %s", address, - entryAnnotationString(entry))); - } + if (annotateTo != null || debugPrint != null) { + annotate(output.getCursor() - mark, + String.format("%04x: +local %s", address, entryAnnotationString(entry))); + } - if (DEBUG) { - System.err.println("emit local start"); - } + if (DEBUG) { + System.err.println("emit local start"); } + } - /** - * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence. - * - * @param entry {@code entry non-null;} entry associated with end. - * @throws IOException - */ - private void emitLocalEnd(LocalList.Entry entry) - throws IOException { + /** + * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED + * DBG_START_LOCAL_EXTENDED} sequence. + * + * @param entry entry to emit + * @throws IOException + */ + private void emitLocalStartExtended(LocalList.Entry entry) throws IOException { - int mark = output.getCursor(); + int mark = output.getCursor(); - output.writeByte(DBG_END_LOCAL); - output.writeUleb128(entry.getRegister()); + output.writeByte(DBG_START_LOCAL_EXTENDED); - if (annotateTo != null || debugPrint != null) { - annotate(output.getCursor() - mark, - String.format("%04x: -local %s", address, - entryAnnotationString(entry))); - } + emitUnsignedLeb128(entry.getRegister()); + emitStringIndex(entry.getName()); + emitTypeIndex(entry.getType()); + emitStringIndex(entry.getSignature()); - if (DEBUG) { - System.err.println("emit local end"); - } + if (annotateTo != null || debugPrint != null) { + annotate(output.getCursor() - mark, + String.format("%04x: +localx %s", address, entryAnnotationString(entry))); } - /** - * Emits the necessary byte sequences to emit the given position table - * entry. This will typically be a single special opcode, although - * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE. - * - * @param entry position entry to emit. - * @throws IOException - */ - private void emitPosition(PositionList.Entry entry) - throws IOException { + if (DEBUG) { + System.err.println("emit local start"); + } + } - SourcePosition pos = entry.getPosition(); - int newLine = pos.getLine(); - int newAddress = entry.getAddress(); + /** + * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence. + * + * @param entry {@code entry non-null;} entry associated with end. + * @throws IOException + */ + private void emitLocalEnd(LocalList.Entry entry) throws IOException { - int opcode; + int mark = output.getCursor(); - int deltaLines = newLine - line; - int deltaAddress = newAddress - address; + output.writeByte(DBG_END_LOCAL); + output.writeUleb128(entry.getRegister()); - if (deltaAddress < 0) { - throw new RuntimeException( - "Position entries must be in ascending address order"); - } - - if ((deltaLines < DBG_LINE_BASE) - || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) { - emitAdvanceLine(deltaLines); - deltaLines = 0; - } + if (annotateTo != null || debugPrint != null) { + annotate(output.getCursor() - mark, + String.format("%04x: -local %s", address, entryAnnotationString(entry))); + } - opcode = computeOpcode (deltaLines, deltaAddress); + if (DEBUG) { + System.err.println("emit local end"); + } + } - if ((opcode & ~0xff) > 0) { - emitAdvancePc(deltaAddress); - deltaAddress = 0; - opcode = computeOpcode (deltaLines, deltaAddress); + /** + * Emits the necessary byte sequences to emit the given position table + * entry. This will typically be a single special opcode, although + * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE. + * + * @param entry position entry to emit. + * @throws IOException + */ + private void emitPosition(PositionList.Entry entry) throws IOException { - if ((opcode & ~0xff) > 0) { - emitAdvanceLine(deltaLines); - deltaLines = 0; - opcode = computeOpcode (deltaLines, deltaAddress); - } - } + SourcePosition pos = entry.getPosition(); + int newLine = pos.getLine(); + int newAddress = entry.getAddress(); - output.writeByte(opcode); + int opcode; - line += deltaLines; - address += deltaAddress; + int deltaLines = newLine - line; + int deltaAddress = newAddress - address; - if (annotateTo != null || debugPrint != null) { - annotate(1, - String.format("%04x: line %d", address, line)); - } + if (deltaAddress < 0) { + throw new RuntimeException("Position entries must be in ascending address order"); } - /** - * Computes a special opcode that will encode the given position change. - * If the return value is > 0xff, then the request cannot be fulfilled. - * Essentially the same as described in "DWARF Debugging Format Version 3" - * section 6.2.5.1. - * - * @param deltaLines {@code >= DBG_LINE_BASE, <= DBG_LINE_BASE + - * DBG_LINE_RANGE;} the line change to encode - * @param deltaAddress {@code >= 0;} the address change to encode - * @return {@code <= 0xff} if in range, otherwise parameters are out - * of range - */ - private static int computeOpcode(int deltaLines, int deltaAddress) { - if (deltaLines < DBG_LINE_BASE - || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) { - - throw new RuntimeException("Parameter out of range"); - } - - return (deltaLines - DBG_LINE_BASE) - + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL; + if ((deltaLines < DBG_LINE_BASE) || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE - 1))) { + emitAdvanceLine(deltaLines); + deltaLines = 0; } - /** - * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE} - * sequence. - * - * @param deltaLines amount to change line number register by - * @throws IOException - */ - private void emitAdvanceLine(int deltaLines) throws IOException { - int mark = output.getCursor(); + opcode = computeOpcode(deltaLines, deltaAddress); - output.writeByte(DBG_ADVANCE_LINE); - output.writeSleb128(deltaLines); - line += deltaLines; + if ((opcode & ~0xff) > 0) { + emitAdvancePc(deltaAddress); + deltaAddress = 0; + opcode = computeOpcode(deltaLines, deltaAddress); - if (annotateTo != null || debugPrint != null) { - annotate(output.getCursor() - mark, - String.format("line = %d", line)); - } - - if (DEBUG) { - System.err.printf("Emitting advance_line for %d\n", deltaLines); - } + if ((opcode & ~0xff) > 0) { + emitAdvanceLine(deltaLines); + deltaLines = 0; + opcode = computeOpcode(deltaLines, deltaAddress); + } } - /** - * Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC} - * sequence. - * - * @param deltaAddress {@code >= 0;} amount to change program counter by - * @throws IOException - */ - private void emitAdvancePc(int deltaAddress) throws IOException { - int mark = output.getCursor(); + output.writeByte(opcode); - output.writeByte(DBG_ADVANCE_PC); - output.writeUleb128(deltaAddress); - address += deltaAddress; + line += deltaLines; + address += deltaAddress; - if (annotateTo != null || debugPrint != null) { - annotate(output.getCursor() - mark, - String.format("%04x: advance pc", address)); - } + if (annotateTo != null || debugPrint != null) { + annotate(1, String.format("%04x: line %d", address, line)); + } + } + + /** + * Computes a special opcode that will encode the given position change. + * If the return value is > 0xff, then the request cannot be fulfilled. + * Essentially the same as described in "DWARF Debugging Format Version 3" + * section 6.2.5.1. + * + * @param deltaLines {@code >= DBG_LINE_BASE, <= DBG_LINE_BASE + + * DBG_LINE_RANGE;} the line change to encode + * @param deltaAddress {@code >= 0;} the address change to encode + * @return {@code <= 0xff} if in range, otherwise parameters are out + * of range + */ + private static int computeOpcode(int deltaLines, int deltaAddress) { + if (deltaLines < DBG_LINE_BASE || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE - 1)) { + + throw new RuntimeException("Parameter out of range"); + } - if (DEBUG) { - System.err.printf("Emitting advance_pc for %d\n", deltaAddress); - } + return (deltaLines - DBG_LINE_BASE) + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL; + } + + /** + * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE} + * sequence. + * + * @param deltaLines amount to change line number register by + * @throws IOException + */ + private void emitAdvanceLine(int deltaLines) throws IOException { + int mark = output.getCursor(); + + output.writeByte(DBG_ADVANCE_LINE); + output.writeSleb128(deltaLines); + line += deltaLines; + + if (annotateTo != null || debugPrint != null) { + annotate(output.getCursor() - mark, String.format("line = %d", line)); } - /** - * Emits an unsigned LEB128 value. - * - * @param n {@code >= 0;} value to emit. Note that, although this can - * represent integers larger than Integer.MAX_VALUE, we currently don't - * allow that. - * @throws IOException - */ - private void emitUnsignedLeb128(int n) throws IOException { - // We'll never need the top end of the unsigned range anyway. - if (n < 0) { - throw new RuntimeException( - "Signed value where unsigned required: " + n); - } + if (DEBUG) { + System.err.printf("Emitting advance_line for %d\n", deltaLines); + } + } + + /** + * Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC} + * sequence. + * + * @param deltaAddress {@code >= 0;} amount to change program counter by + * @throws IOException + */ + private void emitAdvancePc(int deltaAddress) throws IOException { + int mark = output.getCursor(); + + output.writeByte(DBG_ADVANCE_PC); + output.writeUleb128(deltaAddress); + address += deltaAddress; + + if (annotateTo != null || debugPrint != null) { + annotate(output.getCursor() - mark, String.format("%04x: advance pc", address)); + } - output.writeUleb128(n); + if (DEBUG) { + System.err.printf("Emitting advance_pc for %d\n", deltaAddress); + } + } + + /** + * Emits an unsigned LEB128 value. + * + * @param n {@code >= 0;} value to emit. Note that, although this can + * represent integers larger than Integer.MAX_VALUE, we currently don't + * allow that. + * @throws IOException + */ + private void emitUnsignedLeb128(int n) throws IOException { + // We'll never need the top end of the unsigned range anyway. + if (n < 0) { + throw new RuntimeException("Signed value where unsigned required: " + n); } - /** - * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE} - * bytecode. - */ - private void emitEndSequence() { - output.writeByte(DBG_END_SEQUENCE); + output.writeUleb128(n); + } - if (annotateTo != null || debugPrint != null) { - annotate(1, "end sequence"); - } + /** + * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE} + * bytecode. + */ + private void emitEndSequence() { + output.writeByte(DBG_END_SEQUENCE); + + if (annotateTo != null || debugPrint != null) { + annotate(1, "end sequence"); } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/DebugInfoItem.java b/dx/src/com/android/jack/dx/dex/file/DebugInfoItem.java index 61db8d9..8f5ceb6 100644 --- a/dx/src/com/android/jack/dx/dex/file/DebugInfoItem.java +++ b/dx/src/com/android/jack/dx/dex/file/DebugInfoItem.java @@ -26,169 +26,168 @@ import com.android.jack.dx.util.ExceptionWithContext; import java.io.PrintWriter; +/** + * TODO(jack team) + */ public class DebugInfoItem extends OffsettedItem { - /** the required alignment for instances of this class */ - private static final int ALIGNMENT = 1; - - private static final boolean ENABLE_ENCODER_SELF_CHECK = false; - - /** {@code non-null;} the code this item represents */ - private final DalvCode code; - - private byte[] encoded; + /** the required alignment for instances of this class */ + private static final int ALIGNMENT = 1; - private final boolean isStatic; - private final CstMethodRef ref; + private static boolean enableEncoderSelfCheck = false; - public DebugInfoItem(DalvCode code, boolean isStatic, CstMethodRef ref) { - // We don't know the write size yet. - super (ALIGNMENT, -1); - - if (code == null) { - throw new NullPointerException("code == null"); - } - - this.code = code; - this.isStatic = isStatic; - this.ref = ref; - } + /** {@code non-null;} the code this item represents */ + private final DalvCode code; - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_DEBUG_INFO_ITEM; - } + private byte[] encoded; - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - // No contents to add. - } + private final boolean isStatic; + private final CstMethodRef ref; - /** {@inheritDoc} */ - @Override - protected void place0(Section addedTo, int offset) { - // Encode the data and note the size. - - try { - encoded = encode(addedTo.getFile(), null, null, null, false); - setWriteSize(encoded.length); - } catch (RuntimeException ex) { - throw ExceptionWithContext.withContext(ex, - "...while placing debug info for " + ref.toHuman()); - } - } + public DebugInfoItem(DalvCode code, boolean isStatic, CstMethodRef ref) { + // We don't know the write size yet. + super(ALIGNMENT, -1); - /** {@inheritDoc} */ - @Override - public String toHuman() { - throw new RuntimeException("unsupported"); + if (code == null) { + throw new NullPointerException("code == null"); } - /** - * Writes annotations for the elements of this list, as - * zero-length. This is meant to be used for dumping this instance - * directly after a code dump (with the real local list actually - * existing elsewhere in the output). - * - * @param file {@code non-null;} the file to use for referencing other sections - * @param out {@code non-null;} where to annotate to - * @param prefix {@code null-ok;} prefix to attach to each line of output - */ - public void annotateTo(DexFile file, AnnotatedOutput out, String prefix) { - encode(file, prefix, null, out, false); + this.code = code; + this.isStatic = isStatic; + this.ref = ref; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_DEBUG_INFO_ITEM; + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + // No contents to add. + } + + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + // Encode the data and note the size. + + try { + encoded = encode(addedTo.getFile(), null, null, null, false); + setWriteSize(encoded.length); + } catch (RuntimeException ex) { + throw ExceptionWithContext.withContext(ex, + "...while placing debug info for " + ref.toHuman()); } - - /** - * Does a human-friendly dump of this instance. - * - * @param out {@code non-null;} where to dump - * @param prefix {@code non-null;} prefix to attach to each line of output - */ - public void debugPrint(PrintWriter out, String prefix) { - encode(null, prefix, out, null, false); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + throw new RuntimeException("unsupported"); + } + + /** + * Writes annotations for the elements of this list, as + * zero-length. This is meant to be used for dumping this instance + * directly after a code dump (with the real local list actually + * existing elsewhere in the output). + * + * @param file {@code non-null;} the file to use for referencing other sections + * @param out {@code non-null;} where to annotate to + * @param prefix {@code null-ok;} prefix to attach to each line of output + */ + public void annotateTo(DexFile file, AnnotatedOutput out, String prefix) { + encode(file, prefix, null, out, false); + } + + /** + * Does a human-friendly dump of this instance. + * + * @param out {@code non-null;} where to dump + * @param prefix {@code non-null;} prefix to attach to each line of output + */ + public void debugPrint(PrintWriter out, String prefix) { + encode(null, prefix, out, null, false); + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + if (out.annotates()) { + /* + * Re-run the encoder to generate the annotations, + * but write the bits from the original encode + */ + +out.annotate(offsetString() + " debug info"); + encode(file, null, null, out, true); } - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - if (out.annotates()) { - /* - * Re-run the encoder to generate the annotations, - * but write the bits from the original encode - */ - - out.annotate(offsetString() + " debug info"); - encode(file, null, null, out, true); - } - - out.write(encoded); + out.write(encoded); + } + + /** + * Performs debug info encoding. + * + * @param file {@code null-ok;} file to refer to during encoding + * @param prefix {@code null-ok;} prefix to attach to each line of output + * @param debugPrint {@code null-ok;} if specified, an alternate output for + * annotations + * @param out {@code null-ok;} if specified, where annotations should go + * @param consume whether to claim to have consumed output for + * {@code out} + * @return {@code non-null;} the encoded array + */ + private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint, AnnotatedOutput out, + boolean consume) { + byte[] result = encode0(file, prefix, debugPrint, out, consume); + + if (enableEncoderSelfCheck && (file != null)) { + try { + DebugInfoDecoder.validateEncode(result, file, ref, code, isStatic); + } catch (RuntimeException ex) { + // Reconvert, annotating to System.err. + encode0(file, "", new PrintWriter(System.err, true), null, false); + throw ex; + } } - /** - * Performs debug info encoding. - * - * @param file {@code null-ok;} file to refer to during encoding - * @param prefix {@code null-ok;} prefix to attach to each line of output - * @param debugPrint {@code null-ok;} if specified, an alternate output for - * annotations - * @param out {@code null-ok;} if specified, where annotations should go - * @param consume whether to claim to have consumed output for - * {@code out} - * @return {@code non-null;} the encoded array - */ - private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint, - AnnotatedOutput out, boolean consume) { - byte[] result = encode0(file, prefix, debugPrint, out, consume); - - if (ENABLE_ENCODER_SELF_CHECK && (file != null)) { - try { - DebugInfoDecoder.validateEncode(result, file, ref, code, - isStatic); - } catch (RuntimeException ex) { - // Reconvert, annotating to System.err. - encode0(file, "", new PrintWriter(System.err, true), null, - false); - throw ex; - } - } - - return result; + return result; + } + + /** + * Helper for {@link #encode} to do most of the work. + * + * @param file {@code null-ok;} file to refer to during encoding + * @param prefix {@code null-ok;} prefix to attach to each line of output + * @param debugPrint {@code null-ok;} if specified, an alternate output for + * annotations + * @param out {@code null-ok;} if specified, where annotations should go + * @param consume whether to claim to have consumed output for + * {@code out} + * @return {@code non-null;} the encoded array + */ + private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint, AnnotatedOutput out, + boolean consume) { + PositionList positions = code.getPositions(); + LocalList locals = code.getLocals(); + DalvInsnList insns = code.getInsns(); + int codeSize = insns.codeSize(); + int regSize = insns.getRegistersSize(); + + DebugInfoEncoder encoder = + new DebugInfoEncoder(positions, locals, file, codeSize, regSize, isStatic, ref); + + byte[] result; + + if ((debugPrint == null) && (out == null)) { + result = encoder.convert(); + } else { + result = encoder.convertAndAnnotate(prefix, debugPrint, out, consume); } - /** - * Helper for {@link #encode} to do most of the work. - * - * @param file {@code null-ok;} file to refer to during encoding - * @param prefix {@code null-ok;} prefix to attach to each line of output - * @param debugPrint {@code null-ok;} if specified, an alternate output for - * annotations - * @param out {@code null-ok;} if specified, where annotations should go - * @param consume whether to claim to have consumed output for - * {@code out} - * @return {@code non-null;} the encoded array - */ - private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint, - AnnotatedOutput out, boolean consume) { - PositionList positions = code.getPositions(); - LocalList locals = code.getLocals(); - DalvInsnList insns = code.getInsns(); - int codeSize = insns.codeSize(); - int regSize = insns.getRegistersSize(); - - DebugInfoEncoder encoder = - new DebugInfoEncoder(positions, locals, - file, codeSize, regSize, isStatic, ref); - - byte[] result; - - if ((debugPrint == null) && (out == null)) { - result = encoder.convert(); - } else { - result = encoder.convertAndAnnotate(prefix, debugPrint, out, - consume); - } - - return result; - } + return result; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/DexFile.java b/dx/src/com/android/jack/dx/dex/file/DexFile.java index c0fa5b1..027cb25 100644 --- a/dx/src/com/android/jack/dx/dex/file/DexFile.java +++ b/dx/src/com/android/jack/dx/dex/file/DexFile.java @@ -17,6 +17,7 @@ package com.android.jack.dx.dex.file; import com.android.jack.dx.dex.DexOptions; +import com.android.jack.dx.dex.file.MixedItemSection.SortType; import com.android.jack.dx.rop.cst.Constant; import com.android.jack.dx.rop.cst.CstBaseMethodRef; import com.android.jack.dx.rop.cst.CstEnumRef; @@ -37,642 +38,641 @@ import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.zip.Adler32; -import static com.android.jack.dx.dex.file.MixedItemSection.SortType; - /** * Representation of an entire {@code .dex} (Dalvik EXecutable) * file, which itself consists of a set of Dalvik classes. */ public final class DexFile { - /** options controlling the creation of the file */ - private DexOptions dexOptions; - - /** {@code non-null;} word data section */ - private final MixedItemSection wordData; - - /** - * {@code non-null;} type lists section. This is word data, but separating - * it from {@link #wordData} helps break what would otherwise be a - * circular dependency between the that and {@link #protoIds}. - */ - private final MixedItemSection typeLists; - - /** - * {@code non-null;} map section. The map needs to be in a section by itself - * for the self-reference mechanics to work in a reasonably - * straightforward way. See {@link MapItem#addMap} for more detail. - */ - private final MixedItemSection map; - - /** {@code non-null;} string data section */ - private final MixedItemSection stringData; - - /** {@code non-null;} string identifiers section */ - private final StringIdsSection stringIds; - - /** {@code non-null;} type identifiers section */ - private final TypeIdsSection typeIds; - - /** {@code non-null;} prototype identifiers section */ - private final ProtoIdsSection protoIds; - - /** {@code non-null;} field identifiers section */ - private final FieldIdsSection fieldIds; - - /** {@code non-null;} method identifiers section */ - private final MethodIdsSection methodIds; - - /** {@code non-null;} class definitions section */ - private final ClassDefsSection classDefs; - - /** {@code non-null;} class data section */ - private final MixedItemSection classData; - - /** {@code non-null;} byte data section */ - private final MixedItemSection byteData; - - /** {@code non-null;} file header */ - private final HeaderSection header; - - /** - * {@code non-null;} array of sections in the order they will appear in the - * final output file - */ - private final Section[] sections; - - /** {@code >= -1;} total file size or {@code -1} if unknown */ - private int fileSize; - - /** {@code >= 40;} maximum width of the file dump */ - private int dumpWidth; - - /** List of constant index mapping that must be used to remap binary */ - private List<CstIndexMap> cstIndexMaps; - - public DexFile(DexOptions dexOptions, List<CstIndexMap> cstIndexMaps) { - this.cstIndexMaps = cstIndexMaps; - this.dexOptions = dexOptions; - - header = new HeaderSection(this); - typeLists = new MixedItemSection(null, this, 4, SortType.NONE); - wordData = new MixedItemSection("word_data", this, 4, SortType.TYPE); - stringData = - new MixedItemSection("string_data", this, 1, SortType.INSTANCE); - classData = new MixedItemSection(null, this, 1, SortType.NONE); - byteData = new MixedItemSection("byte_data", this, 1, SortType.TYPE); - stringIds = new StringIdsSection(this); - typeIds = new TypeIdsSection(this); - protoIds = new ProtoIdsSection(this); - fieldIds = new FieldIdsSection(this); - methodIds = new MethodIdsSection(this); - classDefs = new ClassDefsSection(this); - map = new MixedItemSection("map", this, 4, SortType.NONE); - - /* - * This is the list of sections in the order they appear in - * the final output. - */ - sections = new Section[] { - header, stringIds, typeIds, protoIds, fieldIds, methodIds, - classDefs, wordData, typeLists, stringData, byteData, - classData, map }; - - fileSize = -1; - dumpWidth = 79; + /** options controlling the creation of the file */ + private DexOptions dexOptions; + + /** {@code non-null;} word data section */ + private final MixedItemSection wordData; + + /** + * {@code non-null;} type lists section. This is word data, but separating + * it from {@link #wordData} helps break what would otherwise be a + * circular dependency between the that and {@link #protoIds}. + */ + private final MixedItemSection typeLists; + + /** + * {@code non-null;} map section. The map needs to be in a section by itself + * for the self-reference mechanics to work in a reasonably + * straightforward way. See {@link MapItem#addMap} for more detail. + */ + private final MixedItemSection map; + + /** {@code non-null;} string data section */ + private final MixedItemSection stringData; + + /** {@code non-null;} string identifiers section */ + private final StringIdsSection stringIds; + + /** {@code non-null;} type identifiers section */ + private final TypeIdsSection typeIds; + + /** {@code non-null;} prototype identifiers section */ + private final ProtoIdsSection protoIds; + + /** {@code non-null;} field identifiers section */ + private final FieldIdsSection fieldIds; + + /** {@code non-null;} method identifiers section */ + private final MethodIdsSection methodIds; + + /** {@code non-null;} class definitions section */ + private final ClassDefsSection classDefs; + + /** {@code non-null;} class data section */ + private final MixedItemSection classData; + + /** {@code non-null;} byte data section */ + private final MixedItemSection byteData; + + /** {@code non-null;} file header */ + private final HeaderSection header; + + /** + * {@code non-null;} array of sections in the order they will appear in the + * final output file + */ + private final Section[] sections; + + /** {@code >= -1;} total file size or {@code -1} if unknown */ + private int fileSize; + + /** {@code >= 40;} maximum width of the file dump */ + private int dumpWidth; + + /** List of constant index mapping that must be used to remap binary */ + private List<CstIndexMap> cstIndexMaps; + + public DexFile(DexOptions dexOptions, List<CstIndexMap> cstIndexMaps) { + this.cstIndexMaps = cstIndexMaps; + this.dexOptions = dexOptions; + + header = new HeaderSection(this); + typeLists = new MixedItemSection(null, this, 4, SortType.NONE); + wordData = new MixedItemSection("word_data", this, 4, SortType.TYPE); + stringData = new MixedItemSection("string_data", this, 1, SortType.INSTANCE); + classData = new MixedItemSection(null, this, 1, SortType.NONE); + byteData = new MixedItemSection("byte_data", this, 1, SortType.TYPE); + stringIds = new StringIdsSection(this); + typeIds = new TypeIdsSection(this); + protoIds = new ProtoIdsSection(this); + fieldIds = new FieldIdsSection(this); + methodIds = new MethodIdsSection(this); + classDefs = new ClassDefsSection(this); + map = new MixedItemSection("map", this, 4, SortType.NONE); + + /* + * This is the list of sections in the order they appear in + * the final output. + */ + sections = new Section[] {header, + stringIds, + typeIds, + protoIds, + fieldIds, + methodIds, + classDefs, + wordData, + typeLists, + stringData, + byteData, + classData, + map}; + + fileSize = -1; + dumpWidth = 79; + } + + /** + * Constructs an instance. It is initially empty. + */ + public DexFile(DexOptions dexOptions) { + this(dexOptions, null); + } + + /** + * Returns true if this dex doesn't contain any class defs. + */ + public boolean isEmpty() { + return classDefs.items().isEmpty(); + } + + /** + * Gets the dex-creation options object. + */ + public DexOptions getDexOptions() { + return dexOptions; + } + + /** + * Adds a class to this instance. It is illegal to attempt to add more + * than one class with the same name. + * + * @param clazz {@code non-null;} the class to add + */ + public void add(ClassDefItem clazz) { + classDefs.add(clazz); + } + + /** + * Gets the class definition with the given name, if any. + * + * @param name {@code non-null;} the class name to look for + * @return {@code null-ok;} the class with the given name, or {@code null} + * if there is no such class + */ + public ClassDefItem getClassOrNull(String name) { + try { + Type type = Type.internClassName(name); + return (ClassDefItem) classDefs.get(new CstType(type)); + } catch (IllegalArgumentException ex) { + // Translate exception, per contract. + return null; } - - /** - * Constructs an instance. It is initially empty. - */ - public DexFile(DexOptions dexOptions) { - this(dexOptions, null); + } + + /** + * Writes the contents of this instance as either a binary or a + * human-readable form, or both. + * + * @param out {@code null-ok;} where to write to + * @param humanOut {@code null-ok;} where to write human-oriented output to + * @param verbose whether to be verbose when writing human-oriented output + */ + public void writeTo(OutputStream out, Writer humanOut, boolean verbose) throws IOException { + boolean annotate = (humanOut != null); + ByteArrayAnnotatedOutput result = toDex0(annotate, verbose); + + if (out != null) { + out.write(result.getArray()); } - /** - * Returns true if this dex doesn't contain any class defs. - */ - public boolean isEmpty() { - return classDefs.items().isEmpty(); + if (annotate) { + result.writeAnnotationsTo(humanOut); } - - /** - * Gets the dex-creation options object. - */ - public DexOptions getDexOptions() { - return dexOptions; + } + + /** + * Returns the contents of this instance as a {@code .dex} file, + * in {@code byte[]} form. + * + * @param humanOut {@code null-ok;} where to write human-oriented output to + * @param verbose whether to be verbose when writing human-oriented output + * @return {@code non-null;} a {@code .dex} file for this instance + */ + public byte[] toDex(Writer humanOut, boolean verbose) throws IOException { + boolean annotate = (humanOut != null); + ByteArrayAnnotatedOutput result = toDex0(annotate, verbose); + + if (annotate) { + result.writeAnnotationsTo(humanOut); } - /** - * Adds a class to this instance. It is illegal to attempt to add more - * than one class with the same name. - * - * @param clazz {@code non-null;} the class to add - */ - public void add(ClassDefItem clazz) { - classDefs.add(clazz); + return result.getArray(); + } + + /** + * Sets the maximum width of the human-oriented dump of the instance. + * + * @param dumpWidth {@code >= 40;} the width + */ + public void setDumpWidth(int dumpWidth) { + if (dumpWidth < 40) { + throw new IllegalArgumentException("dumpWidth < 40"); } - /** - * Gets the class definition with the given name, if any. - * - * @param name {@code non-null;} the class name to look for - * @return {@code null-ok;} the class with the given name, or {@code null} - * if there is no such class - */ - public ClassDefItem getClassOrNull(String name) { - try { - Type type = Type.internClassName(name); - return (ClassDefItem) classDefs.get(new CstType(type)); - } catch (IllegalArgumentException ex) { - // Translate exception, per contract. - return null; - } + this.dumpWidth = dumpWidth; + } + + /** + * Gets the total file size, if known. + * + * <p>This is package-scope in order to allow + * the {@link HeaderSection} to set itself up properly.</p> + * + * @return {@code >= 0;} the total file size + * @throws RuntimeException thrown if the file size is not yet known + */ + /*package*/int getFileSize() { + if (fileSize < 0) { + throw new RuntimeException("file size not yet known"); } - /** - * Writes the contents of this instance as either a binary or a - * human-readable form, or both. - * - * @param out {@code null-ok;} where to write to - * @param humanOut {@code null-ok;} where to write human-oriented output to - * @param verbose whether to be verbose when writing human-oriented output - */ - public void writeTo(OutputStream out, Writer humanOut, boolean verbose) - throws IOException { - boolean annotate = (humanOut != null); - ByteArrayAnnotatedOutput result = toDex0(annotate, verbose); - - if (out != null) { - out.write(result.getArray()); - } - - if (annotate) { - result.writeAnnotationsTo(humanOut); - } + return fileSize; + } + + /** + * Gets the string data section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the string data section + */ + /*package*/MixedItemSection getStringData() { + return stringData; + } + + /** + * Gets the word data section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the word data section + */ + /*package*/MixedItemSection getWordData() { + return wordData; + } + + /** + * Gets the type lists section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the word data section + */ + /*package*/MixedItemSection getTypeLists() { + return typeLists; + } + + /** + * Gets the map section. + * + * <p>This is package-scope in order to allow the header section + * to query it.</p> + * + * @return {@code non-null;} the map section + */ + /*package*/MixedItemSection getMap() { + return map; + } + + /** + * Gets the string identifiers section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the string identifiers section + */ + public StringIdsSection getStringIds() { + return stringIds; + } + + /** + * Gets the class definitions section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the class definitions section + */ + /*package*/ClassDefsSection getClassDefs() { + return classDefs; + } + + /** + * Gets the class data section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the class data section + */ + /*package*/MixedItemSection getClassData() { + return classData; + } + + /** + * Gets the type identifiers section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the class identifiers section + */ + public TypeIdsSection getTypeIds() { + return typeIds; + } + + /** + * Gets the prototype identifiers section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the prototype identifiers section + */ + /*package*/ProtoIdsSection getProtoIds() { + return protoIds; + } + + /** + * Gets the field identifiers section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the field identifiers section + */ + public FieldIdsSection getFieldIds() { + return fieldIds; + } + + /** + * Gets the method identifiers section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the method identifiers section + */ + public MethodIdsSection getMethodIds() { + return methodIds; + } + + /** + * Gets the byte data section. + * + * <p>This is package-scope in order to allow + * the various {@link Item} instances to add items to the + * instance.</p> + * + * @return {@code non-null;} the byte data section + */ + /*package*/MixedItemSection getByteData() { + return byteData; + } + + /** + * Gets the first section of the file that is to be considered + * part of the data section. + * + * <p>This is package-scope in order to allow the header section + * to query it.</p> + * + * @return {@code non-null;} the section + */ + /*package*/Section getFirstDataSection() { + return wordData; + } + + /** + * Gets the last section of the file that is to be considered + * part of the data section. + * + * <p>This is package-scope in order to allow the header section + * to query it.</p> + * + * @return {@code non-null;} the section + */ + /*package*/Section getLastDataSection() { + return map; + } + + /** + * Interns the given constant in the appropriate section of this + * instance, or do nothing if the given constant isn't the sort + * that should be interned. + * + * @param cst {@code non-null;} constant to possibly intern + */ + /*package*/void internIfAppropriate(Constant cst) { + if (cst instanceof CstString) { + stringIds.intern((CstString) cst); + } else if (cst instanceof CstType) { + typeIds.intern((CstType) cst); + } else if (cst instanceof CstBaseMethodRef) { + methodIds.intern((CstBaseMethodRef) cst); + } else if (cst instanceof CstFieldRef) { + fieldIds.intern((CstFieldRef) cst); + } else if (cst instanceof CstEnumRef) { + fieldIds.intern(((CstEnumRef) cst).getFieldRef()); + } else if (cst == null) { + throw new NullPointerException("cst == null"); } - - /** - * Returns the contents of this instance as a {@code .dex} file, - * in {@code byte[]} form. - * - * @param humanOut {@code null-ok;} where to write human-oriented output to - * @param verbose whether to be verbose when writing human-oriented output - * @return {@code non-null;} a {@code .dex} file for this instance - */ - public byte[] toDex(Writer humanOut, boolean verbose) - throws IOException { - boolean annotate = (humanOut != null); - ByteArrayAnnotatedOutput result = toDex0(annotate, verbose); - - if (annotate) { - result.writeAnnotationsTo(humanOut); - } - - return result.getArray(); + } + + /** + * Gets the {@link IndexedItem} corresponding to the given constant, + * if it is a constant that has such a correspondence, or return + * {@code null} if it isn't such a constant. This will throw + * an exception if the given constant <i>should</i> have been found + * but wasn't. + * + * @param cst {@code non-null;} the constant to look up + * @return {@code null-ok;} its corresponding item, if it has a corresponding + * item, or {@code null} if it's not that sort of constant + */ + public IndexedItem findItemOrNull(Constant cst) { + if (cst instanceof CstString) { + return stringIds.get(cst); + } else if (cst instanceof CstType) { + return typeIds.get(cst); + } else if (cst instanceof CstBaseMethodRef) { + return methodIds.get(cst); + } else if (cst instanceof CstFieldRef) { + return fieldIds.get(cst); + } else { + return null; } - - /** - * Sets the maximum width of the human-oriented dump of the instance. - * - * @param dumpWidth {@code >= 40;} the width - */ - public void setDumpWidth(int dumpWidth) { - if (dumpWidth < 40) { - throw new IllegalArgumentException("dumpWidth < 40"); - } - - this.dumpWidth = dumpWidth; + } + + /** + * Returns the contents of this instance as a {@code .dex} file, + * in a {@link ByteArrayAnnotatedOutput} instance. + * + * @param annotate whether or not to keep annotations + * @param verbose if annotating, whether to be verbose + * @return {@code non-null;} a {@code .dex} file for this instance + */ + private ByteArrayAnnotatedOutput toDex0(boolean annotate, boolean verbose) { + /* + * The following is ordered so that the prepare() calls which + * add items happen before the calls to the sections that get + * added to. + */ + +if (cstIndexMaps != null) { + for (CstIndexMap cstIndexMap : cstIndexMaps) { + cstIndexMap.mergeConstantsIntoDexFile(this); + } } - /** - * Gets the total file size, if known. - * - * <p>This is package-scope in order to allow - * the {@link HeaderSection} to set itself up properly.</p> - * - * @return {@code >= 0;} the total file size - * @throws RuntimeException thrown if the file size is not yet known - */ - /*package*/ int getFileSize() { - if (fileSize < 0) { - throw new RuntimeException("file size not yet known"); + classDefs.prepare(); + classData.prepare(); + wordData.prepare(); + byteData.prepare(); + methodIds.prepare(); + fieldIds.prepare(); + protoIds.prepare(); + typeLists.prepare(); + typeIds.prepare(); + stringIds.prepare(); + stringData.prepare(); + header.prepare(); + + // Place the sections within the file. + + int count = sections.length; + int offset = 0; + + for (int i = 0; i < count; i++) { + Section one = sections[i]; + int placedAt = one.setFileOffset(offset); + if (placedAt < offset) { + throw new RuntimeException("bogus placement for section " + i); + } + + try { + if (one == map) { + /* + * Inform the map of all the sections, and add it + * to the file. This can only be done after all + * the other items have been sorted and placed. + */ + MapItem.addMap(sections, map); + map.prepare(); } - return fileSize; - } - - /** - * Gets the string data section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the string data section - */ - /*package*/ MixedItemSection getStringData() { - return stringData; - } - - /** - * Gets the word data section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the word data section - */ - /*package*/ MixedItemSection getWordData() { - return wordData; - } - - /** - * Gets the type lists section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the word data section - */ - /*package*/ MixedItemSection getTypeLists() { - return typeLists; - } - - /** - * Gets the map section. - * - * <p>This is package-scope in order to allow the header section - * to query it.</p> - * - * @return {@code non-null;} the map section - */ - /*package*/ MixedItemSection getMap() { - return map; - } - - /** - * Gets the string identifiers section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the string identifiers section - */ - public StringIdsSection getStringIds() { - return stringIds; - } - - /** - * Gets the class definitions section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the class definitions section - */ - /*package*/ ClassDefsSection getClassDefs() { - return classDefs; - } - - /** - * Gets the class data section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the class data section - */ - /*package*/ MixedItemSection getClassData() { - return classData; - } - - /** - * Gets the type identifiers section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the class identifiers section - */ - public TypeIdsSection getTypeIds() { - return typeIds; - } - - /** - * Gets the prototype identifiers section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the prototype identifiers section - */ - /*package*/ ProtoIdsSection getProtoIds() { - return protoIds; - } + if (one instanceof MixedItemSection) { + /* + * Place the items of a MixedItemSection that just + * got placed. + */ + ((MixedItemSection) one).placeItems(); + } - /** - * Gets the field identifiers section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the field identifiers section - */ - public FieldIdsSection getFieldIds() { - return fieldIds; + offset = placedAt + one.writeSize(); + } catch (RuntimeException ex) { + throw ExceptionWithContext.withContext(ex, "...while writing section " + i); + } } - /** - * Gets the method identifiers section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the method identifiers section - */ - public MethodIdsSection getMethodIds() { - return methodIds; - } + // Write out all the sections. - /** - * Gets the byte data section. - * - * <p>This is package-scope in order to allow - * the various {@link Item} instances to add items to the - * instance.</p> - * - * @return {@code non-null;} the byte data section - */ - /*package*/ MixedItemSection getByteData() { - return byteData; - } + fileSize = offset; + byte[] barr = new byte[fileSize]; + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(barr); - /** - * Gets the first section of the file that is to be considered - * part of the data section. - * - * <p>This is package-scope in order to allow the header section - * to query it.</p> - * - * @return {@code non-null;} the section - */ - /*package*/ Section getFirstDataSection() { - return wordData; + if (annotate) { + out.enableAnnotations(dumpWidth, verbose); } - /** - * Gets the last section of the file that is to be considered - * part of the data section. - * - * <p>This is package-scope in order to allow the header section - * to query it.</p> - * - * @return {@code non-null;} the section - */ - /*package*/ Section getLastDataSection() { - return map; - } - - /** - * Interns the given constant in the appropriate section of this - * instance, or do nothing if the given constant isn't the sort - * that should be interned. - * - * @param cst {@code non-null;} constant to possibly intern - */ - /*package*/ void internIfAppropriate(Constant cst) { - if (cst instanceof CstString) { - stringIds.intern((CstString) cst); - } else if (cst instanceof CstType) { - typeIds.intern((CstType) cst); - } else if (cst instanceof CstBaseMethodRef) { - methodIds.intern((CstBaseMethodRef) cst); - } else if (cst instanceof CstFieldRef) { - fieldIds.intern((CstFieldRef) cst); - } else if (cst instanceof CstEnumRef) { - fieldIds.intern(((CstEnumRef) cst).getFieldRef()); - } else if (cst == null) { - throw new NullPointerException("cst == null"); + for (int i = 0; i < count; i++) { + try { + Section one = sections[i]; + int zeroCount = one.getFileOffset() - out.getCursor(); + if (zeroCount < 0) { + throw new ExceptionWithContext("excess write of " + (-zeroCount)); } - } - - /** - * Gets the {@link IndexedItem} corresponding to the given constant, - * if it is a constant that has such a correspondence, or return - * {@code null} if it isn't such a constant. This will throw - * an exception if the given constant <i>should</i> have been found - * but wasn't. - * - * @param cst {@code non-null;} the constant to look up - * @return {@code null-ok;} its corresponding item, if it has a corresponding - * item, or {@code null} if it's not that sort of constant - */ - public IndexedItem findItemOrNull(Constant cst) { - if (cst instanceof CstString) { - return stringIds.get(cst); - } else if (cst instanceof CstType) { - return typeIds.get(cst); - } else if (cst instanceof CstBaseMethodRef) { - return methodIds.get(cst); - } else if (cst instanceof CstFieldRef) { - return fieldIds.get(cst); + out.writeZeroes(one.getFileOffset() - out.getCursor()); + one.writeTo(out); + } catch (RuntimeException ex) { + ExceptionWithContext ec; + if (ex instanceof ExceptionWithContext) { + ec = (ExceptionWithContext) ex; } else { - return null; + ec = new ExceptionWithContext(ex); } + ec.addContext("...while writing section " + i); + throw ec; + } } - /** - * Returns the contents of this instance as a {@code .dex} file, - * in a {@link ByteArrayAnnotatedOutput} instance. - * - * @param annotate whether or not to keep annotations - * @param verbose if annotating, whether to be verbose - * @return {@code non-null;} a {@code .dex} file for this instance - */ - private ByteArrayAnnotatedOutput toDex0(boolean annotate, - boolean verbose) { - /* - * The following is ordered so that the prepare() calls which - * add items happen before the calls to the sections that get - * added to. - */ - - if (cstIndexMaps != null) { - for (CstIndexMap cstIndexMap : cstIndexMaps) { - cstIndexMap.mergeConstantsIntoDexFile(this); - } - } - - classDefs.prepare(); - classData.prepare(); - wordData.prepare(); - byteData.prepare(); - methodIds.prepare(); - fieldIds.prepare(); - protoIds.prepare(); - typeLists.prepare(); - typeIds.prepare(); - stringIds.prepare(); - stringData.prepare(); - header.prepare(); - - // Place the sections within the file. - - int count = sections.length; - int offset = 0; - - for (int i = 0; i < count; i++) { - Section one = sections[i]; - int placedAt = one.setFileOffset(offset); - if (placedAt < offset) { - throw new RuntimeException("bogus placement for section " + i); - } - - try { - if (one == map) { - /* - * Inform the map of all the sections, and add it - * to the file. This can only be done after all - * the other items have been sorted and placed. - */ - MapItem.addMap(sections, map); - map.prepare(); - } - - if (one instanceof MixedItemSection) { - /* - * Place the items of a MixedItemSection that just - * got placed. - */ - ((MixedItemSection) one).placeItems(); - } - - offset = placedAt + one.writeSize(); - } catch (RuntimeException ex) { - throw ExceptionWithContext.withContext(ex, - "...while writing section " + i); - } - } - - // Write out all the sections. - - fileSize = offset; - byte[] barr = new byte[fileSize]; - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(barr); - - if (annotate) { - out.enableAnnotations(dumpWidth, verbose); - } - - for (int i = 0; i < count; i++) { - try { - Section one = sections[i]; - int zeroCount = one.getFileOffset() - out.getCursor(); - if (zeroCount < 0) { - throw new ExceptionWithContext("excess write of " + - (-zeroCount)); - } - out.writeZeroes(one.getFileOffset() - out.getCursor()); - one.writeTo(out); - } catch (RuntimeException ex) { - ExceptionWithContext ec; - if (ex instanceof ExceptionWithContext) { - ec = (ExceptionWithContext) ex; - } else { - ec = new ExceptionWithContext(ex); - } - ec.addContext("...while writing section " + i); - throw ec; - } - } - - if (out.getCursor() != fileSize) { - throw new RuntimeException("foreshortened write"); - } - - // Perform final bookkeeping. - - calcSignature(barr); - calcChecksum(barr); - - if (annotate) { - wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM, - "\nmethod code index:\n\n"); - getStatistics().writeAnnotation(out); - out.finishAnnotating(); - } - - return out; + if (out.getCursor() != fileSize) { + throw new RuntimeException("foreshortened write"); } - /** - * Generates and returns statistics for all the items in the file. - * - * @return {@code non-null;} the statistics - */ - public Statistics getStatistics() { - Statistics stats = new Statistics(); + // Perform final bookkeeping. - for (Section s : sections) { - stats.addAll(s); - } + calcSignature(barr); + calcChecksum(barr); - return stats; + if (annotate) { + wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM, "\nmethod code index:\n\n"); + getStatistics().writeAnnotation(out); + out.finishAnnotating(); } - /** - * Calculates the signature for the {@code .dex} file in the - * given array, and modify the array to contain it. - * - * @param bytes {@code non-null;} the bytes of the file - */ - private static void calcSignature(byte[] bytes) { - MessageDigest md; + return out; + } - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException(ex); - } - - md.update(bytes, 32, bytes.length - 32); + /** + * Generates and returns statistics for all the items in the file. + * + * @return {@code non-null;} the statistics + */ + public Statistics getStatistics() { + Statistics stats = new Statistics(); - 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); - } + for (Section s : sections) { + stats.addAll(s); } - /** - * Calculates the checksum for the {@code .dex} file in the - * given array, and modify the array to contain it. - * - * @param bytes {@code non-null;} the bytes of the file - */ - private static void calcChecksum(byte[] bytes) { - Adler32 a32 = new Adler32(); - - a32.update(bytes, 12, bytes.length - 12); + return stats; + } + + /** + * Calculates the signature for the {@code .dex} file in the + * given array, and modify the array to contain it. + * + * @param bytes {@code non-null;} the bytes of the file + */ + private static void calcSignature(byte[] bytes) { + MessageDigest md; + + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } - int sum = (int) a32.getValue(); + md.update(bytes, 32, bytes.length - 32); - bytes[8] = (byte) sum; - bytes[9] = (byte) (sum >> 8); - bytes[10] = (byte) (sum >> 16); - bytes[11] = (byte) (sum >> 24); + 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} file in the + * given array, and modify the array to contain it. + * + * @param bytes {@code non-null;} the bytes of the file + */ + private static void calcChecksum(byte[] bytes) { + Adler32 a32 = new Adler32(); + + a32.update(bytes, 12, bytes.length - 12); + + int sum = (int) a32.getValue(); + + bytes[8] = (byte) sum; + bytes[9] = (byte) (sum >> 8); + bytes[10] = (byte) (sum >> 16); + bytes[11] = (byte) (sum >> 24); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/EncodedArrayItem.java b/dx/src/com/android/jack/dx/dex/file/EncodedArrayItem.java index b87b7cc..7bad073 100644 --- a/dx/src/com/android/jack/dx/dex/file/EncodedArrayItem.java +++ b/dx/src/com/android/jack/dx/dex/file/EncodedArrayItem.java @@ -24,99 +24,100 @@ import com.android.jack.dx.util.ByteArrayAnnotatedOutput; * Encoded array of constant values. */ public final class EncodedArrayItem extends OffsettedItem { - /** the required alignment for instances of this class */ - private static final int ALIGNMENT = 1; - - /** {@code non-null;} the array to represent */ - private final CstArray array; - - /** - * {@code null-ok;} encoded form, ready for writing to a file; set during - * {@link #place0} + /** the required alignment for instances of this class */ + private static final int ALIGNMENT = 1; + + /** {@code non-null;} the array to represent */ + private final CstArray array; + + /** + * {@code null-ok;} encoded form, ready for writing to a file; set during + * {@link #place0} + */ + private byte[] encodedForm; + + /** + * Constructs an instance. + * + * @param array {@code non-null;} array to represent + */ + public EncodedArrayItem(CstArray array) { + /* + * The write size isn't known up-front because (the variable-lengthed) + * leb128 type is used to represent some things. */ - private byte[] encodedForm; - - /** - * Constructs an instance. - * - * @param array {@code non-null;} array to represent - */ - public EncodedArrayItem(CstArray array) { - /* - * The write size isn't known up-front because (the variable-lengthed) - * leb128 type is used to represent some things. - */ - super(ALIGNMENT, -1); - - if (array == null) { - throw new NullPointerException("array == null"); - } - - this.array = array; - this.encodedForm = null; - } - - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_ENCODED_ARRAY_ITEM; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return array.hashCode(); - } - - /** {@inheritDoc} */ - @Override - protected int compareTo0(OffsettedItem other) { - EncodedArrayItem otherArray = (EncodedArrayItem) other; - - return array.compareTo(otherArray.array); - } - - /** {@inheritDoc} */ - @Override - public String toHuman() { - return array.toHuman(); - } - - /** {@inheritDoc} */ - public void addContents(DexFile file) { - ValueEncoder.addContents(file, array); - } - - /** {@inheritDoc} */ - @Override - protected void place0(Section addedTo, int offset) { - // Encode the data and note the size. - - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); - ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out); + super(ALIGNMENT, -1); - encoder.writeArray(array, false); - encodedForm = out.toByteArray(); - setWriteSize(encodedForm.length); + if (array == null) { + throw new NullPointerException("array == null"); } - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - boolean annotates = out.annotates(); - - if (annotates) { - out.annotate(0, offsetString() + " encoded array"); - - /* - * The output is to be annotated, so redo the work previously - * done by place0(), except this time annotations will actually - * get emitted. - */ - ValueEncoder encoder = new ValueEncoder(file, out); - encoder.writeArray(array, true); - } else { - out.write(encodedForm); - } + this.array = array; + this.encodedForm = null; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_ENCODED_ARRAY_ITEM; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return array.hashCode(); + } + + /** {@inheritDoc} */ + @Override + protected int compareTo0(OffsettedItem other) { + EncodedArrayItem otherArray = (EncodedArrayItem) other; + + return array.compareTo(otherArray.array); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return array.toHuman(); + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + ValueEncoder.addContents(file, array); + } + + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + // Encode the data and note the size. + + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out); + + encoder.writeArray(array, false); + encodedForm = out.toByteArray(); + setWriteSize(encodedForm.length); + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + boolean annotates = out.annotates(); + + if (annotates) { + out.annotate(0, offsetString() + " encoded array"); + + /* + * The output is to be annotated, so redo the work previously + * done by place0(), except this time annotations will actually + * get emitted. + */ + ValueEncoder encoder = new ValueEncoder(file, out); + encoder.writeArray(array, true); + } else { + out.write(encodedForm); } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/EncodedField.java b/dx/src/com/android/jack/dx/dex/file/EncodedField.java index 29a6484..cc3d068 100644 --- a/dx/src/com/android/jack/dx/dex/file/EncodedField.java +++ b/dx/src/com/android/jack/dx/dex/file/EncodedField.java @@ -28,127 +28,126 @@ import java.io.PrintWriter; /** * Representation of a field of a class, of any sort. */ -public final class EncodedField extends EncodedMember - implements Comparable<EncodedField> { - /** {@code non-null;} constant for the field */ - private final CstFieldRef field; - - /** - * Constructs an instance. - * - * @param field {@code non-null;} constant for the field - * @param accessFlags access flags - */ - public EncodedField(CstFieldRef field, int accessFlags) { - super(accessFlags); - - if (field == null) { - throw new NullPointerException("field == null"); - } - - /* - * TODO: Maybe check accessFlags, at least for - * easily-checked stuff? - */ - - this.field = field; - } - - /** {@inheritDoc} */ - public int hashCode() { - return field.hashCode(); - } - - /** {@inheritDoc} */ - public boolean equals(Object other) { - if (! (other instanceof EncodedField)) { - return false; - } - - return compareTo((EncodedField) other) == 0; +public final class EncodedField extends EncodedMember implements Comparable<EncodedField> { + /** {@code non-null;} constant for the field */ + private final CstFieldRef field; + + /** + * Constructs an instance. + * + * @param field {@code non-null;} constant for the field + * @param accessFlags access flags + */ + public EncodedField(CstFieldRef field, int accessFlags) { + super(accessFlags); + + if (field == null) { + throw new NullPointerException("field == null"); } - /** - * {@inheritDoc} - * - * <p><b>Note:</b> This compares the method constants only, - * ignoring any associated code, because it should never be the - * case that two different items with the same method constant - * ever appear in the same list (or same file, even).</p> + /* + * TODO(dx team): Maybe check accessFlags, at least for + * easily-checked stuff? */ - public int compareTo(EncodedField other) { - return field.compareTo(other.field); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(100); - - sb.append(getClass().getName()); - sb.append('{'); - sb.append(Hex.u2(getAccessFlags())); - sb.append(' '); - sb.append(field); - sb.append('}'); - return sb.toString(); - } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - FieldIdsSection fieldIds = file.getFieldIds(); - fieldIds.intern(field); - } +this.field = field; + } - /** {@inheritDoc} */ - @Override - public CstString getName() { - return field.getNat().getName(); - } + /** {@inheritDoc} */ + @Override + public int hashCode() { + return field.hashCode(); + } - /** {@inheritDoc} */ - public String toHuman() { - return field.toHuman(); + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof EncodedField)) { + return false; } - /** {@inheritDoc} */ - @Override - public void debugPrint(PrintWriter out, boolean verbose) { - // TODO: Maybe put something better here? - out.println(toString()); + return compareTo((EncodedField) other) == 0; + } + + /** + * {@inheritDoc} + * + * <p><b>Note:</b> This compares the method constants only, + * ignoring any associated code, because it should never be the + * case that two different items with the same method constant + * ever appear in the same list (or same file, even).</p> + */ + @Override + public int compareTo(EncodedField other) { + return field.compareTo(other.field); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(100); + + sb.append(getClass().getName()); + sb.append('{'); + sb.append(Hex.u2(getAccessFlags())); + sb.append(' '); + sb.append(field); + sb.append('}'); + return sb.toString(); + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + FieldIdsSection fieldIds = file.getFieldIds(); + fieldIds.intern(field); + } + + /** {@inheritDoc} */ + @Override + public CstString getName() { + return field.getNat().getName(); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return field.toHuman(); + } + + /** {@inheritDoc} */ + @Override + public void debugPrint(PrintWriter out, boolean verbose) { + // TODO(dx team): Maybe put something better here? + out.println(toString()); + } + + /** + * Gets the constant for the field. + * + * @return {@code non-null;} the constant + */ + public CstFieldRef getRef() { + return field; + } + + /** {@inheritDoc} */ + @Override + public int encode(DexFile file, AnnotatedOutput out, int lastIndex, int dumpSeq) { + int fieldIdx = file.getFieldIds().indexOf(field); + int diff = fieldIdx - lastIndex; + int accessFlags = getAccessFlags(); + + if (out.annotates()) { + out.annotate(0, String.format(" [%x] %s", dumpSeq, field.toHuman())); + out.annotate(Leb128Utils.unsignedLeb128Size(diff), " field_idx: " + Hex.u4(fieldIdx)); + out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags), + " access_flags: " + AccessFlags.fieldString(accessFlags)); } - /** - * Gets the constant for the field. - * - * @return {@code non-null;} the constant - */ - public CstFieldRef getRef() { - return field; - } + out.writeUleb128(diff); + out.writeUleb128(accessFlags); - /** {@inheritDoc} */ - @Override - public int encode(DexFile file, AnnotatedOutput out, - int lastIndex, int dumpSeq) { - int fieldIdx = file.getFieldIds().indexOf(field); - int diff = fieldIdx - lastIndex; - int accessFlags = getAccessFlags(); - - if (out.annotates()) { - out.annotate(0, String.format(" [%x] %s", dumpSeq, - field.toHuman())); - out.annotate(Leb128Utils.unsignedLeb128Size(diff), - " field_idx: " + Hex.u4(fieldIdx)); - out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags), - " access_flags: " + - AccessFlags.fieldString(accessFlags)); - } - - out.writeUleb128(diff); - out.writeUleb128(accessFlags); - - return fieldIdx; - } + return fieldIdx; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/EncodedMember.java b/dx/src/com/android/jack/dx/dex/file/EncodedMember.java index 6502d0f..0a1a962 100644 --- a/dx/src/com/android/jack/dx/dex/file/EncodedMember.java +++ b/dx/src/com/android/jack/dx/dex/file/EncodedMember.java @@ -27,60 +27,59 @@ import java.io.PrintWriter; * purposes of encoding it inside a {@link ClassDataItem}. */ public abstract class EncodedMember implements ToHuman { - /** access flags */ - private final int accessFlags; + /** access flags */ + private final int accessFlags; - /** - * Constructs an instance. - * - * @param accessFlags access flags for the member - */ - public EncodedMember(int accessFlags) { - this.accessFlags = accessFlags; - } + /** + * Constructs an instance. + * + * @param accessFlags access flags for the member + */ + public EncodedMember(int accessFlags) { + this.accessFlags = accessFlags; + } - /** - * Gets the access flags. - * - * @return the access flags - */ - public final int getAccessFlags() { - return accessFlags; - } + /** + * Gets the access flags. + * + * @return the access flags + */ + public final int getAccessFlags() { + return accessFlags; + } - /** - * Gets the name. - * - * @return {@code non-null;} the name - */ - public abstract CstString getName(); + /** + * Gets the name. + * + * @return {@code non-null;} the name + */ + public abstract CstString getName(); - /** - * Does a human-friendly dump of this instance. - * - * @param out {@code non-null;} where to dump - * @param verbose whether to be verbose with the output - */ - public abstract void debugPrint(PrintWriter out, boolean verbose); + /** + * Does a human-friendly dump of this instance. + * + * @param out {@code non-null;} where to dump + * @param verbose whether to be verbose with the output + */ + public abstract void debugPrint(PrintWriter out, boolean verbose); - /** - * Populates a {@link DexFile} with items from within this instance. - * - * @param file {@code non-null;} the file to populate - */ - public abstract void addContents(DexFile file); + /** + * Populates a {@link DexFile} with items from within this instance. + * + * @param file {@code non-null;} the file to populate + */ + public abstract void addContents(DexFile file); - /** - * Encodes this instance to the given output. - * - * @param file {@code non-null;} file this instance is part of - * @param out {@code non-null;} where to write to - * @param lastIndex {@code >= 0;} the previous member index value encoded, or - * {@code 0} if this is the first element to encode - * @param dumpSeq {@code >= 0;} sequence number of this instance for - * annotation purposes - * @return {@code >= 0;} the member index value that was encoded - */ - public abstract int encode(DexFile file, AnnotatedOutput out, - int lastIndex, int dumpSeq); + /** + * Encodes this instance to the given output. + * + * @param file {@code non-null;} file this instance is part of + * @param out {@code non-null;} where to write to + * @param lastIndex {@code >= 0;} the previous member index value encoded, or + * {@code 0} if this is the first element to encode + * @param dumpSeq {@code >= 0;} sequence number of this instance for + * annotation purposes + * @return {@code >= 0;} the member index value that was encoded + */ + public abstract int encode(DexFile file, AnnotatedOutput out, int lastIndex, int dumpSeq); } diff --git a/dx/src/com/android/jack/dx/dex/file/EncodedMethod.java b/dx/src/com/android/jack/dx/dex/file/EncodedMethod.java index 924a604..c75fa04 100644 --- a/dx/src/com/android/jack/dx/dex/file/EncodedMethod.java +++ b/dx/src/com/android/jack/dx/dex/file/EncodedMethod.java @@ -28,164 +28,157 @@ import java.io.PrintWriter; /** * Class that representats a method of a class. */ -public final class EncodedMethod extends EncodedMember - implements Comparable<EncodedMethod> { - /** {@code non-null;} constant for the method */ - private final CstMethodRef method; - - /** - * {@code null-ok;} code for the method, if the method is neither - * {@code abstract} nor {@code native} - */ - private final OffsettedItem code; - - /** - * Constructs an instance. - * - * @param method {@code non-null;} constant for the method - * @param accessFlags access flags - * @param code {@code null-ok;} code for the method, if it is neither - * {@code abstract} nor {@code native} - */ - public EncodedMethod(CstMethodRef method, int accessFlags, OffsettedItem code) { - super(accessFlags); - - assert code == null || code instanceof Code; - - if (method == null) { - throw new NullPointerException("method == null"); - } - - this.method = method; - - this.code = code; +public final class EncodedMethod extends EncodedMember implements Comparable<EncodedMethod> { + /** {@code non-null;} constant for the method */ + private final CstMethodRef method; + + /** + * {@code null-ok;} code for the method, if the method is neither + * {@code abstract} nor {@code native} + */ + private final OffsettedItem code; + + /** + * Constructs an instance. + * + * @param method {@code non-null;} constant for the method + * @param accessFlags access flags + * @param code {@code null-ok;} code for the method, if it is neither + * {@code abstract} nor {@code native} + */ + public EncodedMethod(CstMethodRef method, int accessFlags, OffsettedItem code) { + super(accessFlags); + + assert code == null || code instanceof Code; + + if (method == null) { + throw new NullPointerException("method == null"); } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (! (other instanceof EncodedMethod)) { - return false; - } + this.method = method; - return compareTo((EncodedMethod) other) == 0; - } + this.code = code; + } - /** - * {@inheritDoc} - * - * <p><b>Note:</b> This compares the method constants only, - * ignoring any associated code, because it should never be the - * case that two different items with the same method constant - * ever appear in the same list (or same file, even).</p> - */ - @Override - public int compareTo(EncodedMethod other) { - return method.compareTo(other.method); + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof EncodedMethod)) { + return false; } - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(100); - - sb.append(getClass().getName()); - sb.append('{'); - sb.append(Hex.u2(getAccessFlags())); - sb.append(' '); - sb.append(method); - - if (code != null) { - sb.append(' '); - sb.append(code); - } - - sb.append('}'); - - return sb.toString(); + return compareTo((EncodedMethod) other) == 0; + } + + /** + * {@inheritDoc} + * + * <p><b>Note:</b> This compares the method constants only, + * ignoring any associated code, because it should never be the + * case that two different items with the same method constant + * ever appear in the same list (or same file, even).</p> + */ + @Override + public int compareTo(EncodedMethod other) { + return method.compareTo(other.method); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(100); + + sb.append(getClass().getName()); + sb.append('{'); + sb.append(Hex.u2(getAccessFlags())); + sb.append(' '); + sb.append(method); + + if (code != null) { + sb.append(' '); + sb.append(code); } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - MethodIdsSection methodIds = file.getMethodIds(); - MixedItemSection wordData = file.getWordData(); + sb.append('}'); - methodIds.intern(method); + return sb.toString(); + } - if (code != null) { - wordData.add(code); - } - } + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + MethodIdsSection methodIds = file.getMethodIds(); + MixedItemSection wordData = file.getWordData(); - /** {@inheritDoc} */ - @Override - public final String toHuman() { - return method.toHuman(); - } + methodIds.intern(method); - /** {@inheritDoc} */ - @Override - public final CstString getName() { - return method.getNat().getName(); + if (code != null) { + wordData.add(code); } - - /** {@inheritDoc} */ - @Override - public void debugPrint(PrintWriter out, boolean verbose) { - if (code == null) { - out.println(getRef().toHuman() + ": abstract or native"); - } else { - ((Code) code).debugPrint(out, " ", verbose); - } + } + + /** {@inheritDoc} */ + @Override + public final String toHuman() { + return method.toHuman(); + } + + /** {@inheritDoc} */ + @Override + public final CstString getName() { + return method.getNat().getName(); + } + + /** {@inheritDoc} */ + @Override + public void debugPrint(PrintWriter out, boolean verbose) { + if (code == null) { + out.println(getRef().toHuman() + ": abstract or native"); + } else { + ((Code) code).debugPrint(out, " ", verbose); } - - /** - * Gets the constant for the method. - * - * @return {@code non-null;} the constant + } + + /** + * Gets the constant for the method. + * + * @return {@code non-null;} the constant + */ + public final CstMethodRef getRef() { + return method; + } + + /** {@inheritDoc} */ + @Override + public int encode(DexFile file, AnnotatedOutput out, int lastIndex, int dumpSeq) { + int methodIdx = file.getMethodIds().indexOf(method); + int diff = methodIdx - lastIndex; + int accessFlags = getAccessFlags(); + int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code); + boolean hasCode = (codeOff != 0); + boolean shouldHaveCode = + (accessFlags & (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0; + + /* + * Verify that code appears if and only if a method is + * declared to have it. */ - public final CstMethodRef getRef() { - return method; + if (hasCode != shouldHaveCode) { + throw new UnsupportedOperationException("code vs. access_flags mismatch"); } - /** {@inheritDoc} */ - @Override - public int encode(DexFile file, AnnotatedOutput out, - int lastIndex, int dumpSeq) { - int methodIdx = file.getMethodIds().indexOf(method); - int diff = methodIdx - lastIndex; - int accessFlags = getAccessFlags(); - int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code); - boolean hasCode = (codeOff != 0); - boolean shouldHaveCode = (accessFlags & - (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0; - - /* - * Verify that code appears if and only if a method is - * declared to have it. - */ - if (hasCode != shouldHaveCode) { - throw new UnsupportedOperationException( - "code vs. access_flags mismatch"); - } - - if (out.annotates()) { - out.annotate(0, String.format(" [%x] %s", dumpSeq, - method.toHuman())); - out.annotate(Leb128Utils.unsignedLeb128Size(diff), - " method_idx: " + Hex.u4(methodIdx)); - out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags), - " access_flags: " + - AccessFlags.methodString(accessFlags)); - out.annotate(Leb128Utils.unsignedLeb128Size(codeOff), - " code_off: " + Hex.u4(codeOff)); - } - - out.writeUleb128(diff); - out.writeUleb128(accessFlags); - out.writeUleb128(codeOff); - - return methodIdx; + if (out.annotates()) { + out.annotate(0, String.format(" [%x] %s", dumpSeq, method.toHuman())); + out.annotate(Leb128Utils.unsignedLeb128Size(diff), " method_idx: " + Hex.u4(methodIdx)); + out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags), + " access_flags: " + AccessFlags.methodString(accessFlags)); + out.annotate(Leb128Utils.unsignedLeb128Size(codeOff), " code_off: " + Hex.u4(codeOff)); } + + out.writeUleb128(diff); + out.writeUleb128(accessFlags); + out.writeUleb128(codeOff); + + return methodIdx; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/FieldAnnotationStruct.java b/dx/src/com/android/jack/dx/dex/file/FieldAnnotationStruct.java index 8d11f73..afe4c56 100644 --- a/dx/src/com/android/jack/dx/dex/file/FieldAnnotationStruct.java +++ b/dx/src/com/android/jack/dx/dex/file/FieldAnnotationStruct.java @@ -25,98 +25,99 @@ import com.android.jack.dx.util.ToHuman; /** * Association of a field and its annotations. */ -public final class FieldAnnotationStruct - implements ToHuman, Comparable<FieldAnnotationStruct> { - /** {@code non-null;} the field in question */ - private final CstFieldRef field; - - /** {@code non-null;} the associated annotations */ - private AnnotationSetItem annotations; - - /** - * Constructs an instance. - * - * @param field {@code non-null;} the field in question - * @param annotations {@code non-null;} the associated annotations - */ - public FieldAnnotationStruct(CstFieldRef field, - AnnotationSetItem annotations) { - if (field == null) { - throw new NullPointerException("field == null"); - } - - if (annotations == null) { - throw new NullPointerException("annotations == null"); - } - - this.field = field; - this.annotations = annotations; +public final class FieldAnnotationStruct implements ToHuman, Comparable<FieldAnnotationStruct> { + /** {@code non-null;} the field in question */ + private final CstFieldRef field; + + /** {@code non-null;} the associated annotations */ + private AnnotationSetItem annotations; + + /** + * Constructs an instance. + * + * @param field {@code non-null;} the field in question + * @param annotations {@code non-null;} the associated annotations + */ + public FieldAnnotationStruct(CstFieldRef field, AnnotationSetItem annotations) { + if (field == null) { + throw new NullPointerException("field == null"); } - /** {@inheritDoc} */ - public int hashCode() { - return field.hashCode(); + if (annotations == null) { + throw new NullPointerException("annotations == null"); } - /** {@inheritDoc} */ - public boolean equals(Object other) { - if (! (other instanceof FieldAnnotationStruct)) { - return false; - } - - return field.equals(((FieldAnnotationStruct) other).field); - } - - /** {@inheritDoc} */ - public int compareTo(FieldAnnotationStruct other) { - return field.compareTo(other.field); + this.field = field; + this.annotations = annotations; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return field.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof FieldAnnotationStruct)) { + return false; } - /** {@inheritDoc} */ - public void addContents(DexFile file) { - FieldIdsSection fieldIds = file.getFieldIds(); - MixedItemSection wordData = file.getWordData(); - - fieldIds.intern(field); - annotations = wordData.intern(annotations); + return field.equals(((FieldAnnotationStruct) other).field); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(FieldAnnotationStruct other) { + return field.compareTo(other.field); + } + + /** {@inheritDoc} */ + public void addContents(DexFile file) { + FieldIdsSection fieldIds = file.getFieldIds(); + MixedItemSection wordData = file.getWordData(); + + fieldIds.intern(field); + annotations = wordData.intern(annotations); + } + + /** {@inheritDoc} */ + public void writeTo(DexFile file, AnnotatedOutput out) { + int fieldIdx = file.getFieldIds().indexOf(field); + int annotationsOff = annotations.getAbsoluteOffset(); + + if (out.annotates()) { + out.annotate(0, " " + field.toHuman()); + out.annotate(4, " field_idx: " + Hex.u4(fieldIdx)); + out.annotate(4, " annotations_off: " + Hex.u4(annotationsOff)); } - /** {@inheritDoc} */ - public void writeTo(DexFile file, AnnotatedOutput out) { - int fieldIdx = file.getFieldIds().indexOf(field); - int annotationsOff = annotations.getAbsoluteOffset(); - - if (out.annotates()) { - out.annotate(0, " " + field.toHuman()); - out.annotate(4, " field_idx: " + Hex.u4(fieldIdx)); - out.annotate(4, " annotations_off: " + - Hex.u4(annotationsOff)); - } - - out.writeInt(fieldIdx); - out.writeInt(annotationsOff); - } - - /** {@inheritDoc} */ - public String toHuman() { - return field.toHuman() + ": " + annotations; - } - - /** - * Gets the field this item is for. - * - * @return {@code non-null;} the field - */ - public CstFieldRef getField() { - return field; - } - - /** - * Gets the associated annotations. - * - * @return {@code non-null;} the annotations - */ - public Annotations getAnnotations() { - return annotations.getAnnotations(); - } + out.writeInt(fieldIdx); + out.writeInt(annotationsOff); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return field.toHuman() + ": " + annotations; + } + + /** + * Gets the field this item is for. + * + * @return {@code non-null;} the field + */ + public CstFieldRef getField() { + return field; + } + + /** + * Gets the associated annotations. + * + * @return {@code non-null;} the annotations + */ + public Annotations getAnnotations() { + return annotations.getAnnotations(); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/FieldIdItem.java b/dx/src/com/android/jack/dx/dex/file/FieldIdItem.java index 373ae6c..df58131 100644 --- a/dx/src/com/android/jack/dx/dex/file/FieldIdItem.java +++ b/dx/src/com/android/jack/dx/dex/file/FieldIdItem.java @@ -22,49 +22,49 @@ import com.android.jack.dx.rop.cst.CstFieldRef; * Representation of a field reference inside a Dalvik file. */ public final class FieldIdItem extends MemberIdItem { - /** - * Constructs an instance. - * - * @param field {@code non-null;} the constant for the field - */ - public FieldIdItem(CstFieldRef field) { - super(field); - } + /** + * Constructs an instance. + * + * @param field {@code non-null;} the constant for the field + */ + public FieldIdItem(CstFieldRef field) { + super(field); + } - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_FIELD_ID_ITEM; - } + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_FIELD_ID_ITEM; + } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - super.addContents(file); + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + super.addContents(file); - TypeIdsSection typeIds = file.getTypeIds(); - typeIds.intern(getFieldRef().getType()); - } + TypeIdsSection typeIds = file.getTypeIds(); + typeIds.intern(getFieldRef().getType()); + } - /** - * Gets the field constant. - * - * @return {@code non-null;} the constant - */ - public CstFieldRef getFieldRef() { - return (CstFieldRef) getRef(); - } + /** + * Gets the field constant. + * + * @return {@code non-null;} the constant + */ + public CstFieldRef getFieldRef() { + return (CstFieldRef) getRef(); + } - /** {@inheritDoc} */ - @Override - protected int getTypoidIdx(DexFile file) { - TypeIdsSection typeIds = file.getTypeIds(); - return typeIds.indexOf(getFieldRef().getType()); - } + /** {@inheritDoc} */ + @Override + protected int getTypoidIdx(DexFile file) { + TypeIdsSection typeIds = file.getTypeIds(); + return typeIds.indexOf(getFieldRef().getType()); + } - /** {@inheritDoc} */ - @Override - protected String getTypoidName() { - return "type_idx"; - } + /** {@inheritDoc} */ + @Override + protected String getTypoidName() { + return "type_idx"; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/FieldIdsSection.java b/dx/src/com/android/jack/dx/dex/file/FieldIdsSection.java index 66d30f4..d623b74 100644 --- a/dx/src/com/android/jack/dx/dex/file/FieldIdsSection.java +++ b/dx/src/com/android/jack/dx/dex/file/FieldIdsSection.java @@ -28,110 +28,110 @@ import java.util.TreeMap; * Field refs list section of a {@code .dex} file. */ public final class FieldIdsSection extends MemberIdsSection { - /** - * {@code non-null;} map from field constants to {@link - * FieldIdItem} instances - */ - private final TreeMap<CstFieldRef, FieldIdItem> fieldIds; - - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param file {@code non-null;} file that this instance is part of - */ - public FieldIdsSection(DexFile file) { - super("field_ids", file); - - fieldIds = new TreeMap<CstFieldRef, FieldIdItem>(); + /** + * {@code non-null;} map from field constants to {@link + * FieldIdItem} instances + */ + private final TreeMap<CstFieldRef, FieldIdItem> fieldIds; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param file {@code non-null;} file that this instance is part of + */ + public FieldIdsSection(DexFile file) { + super("field_ids", file); + + fieldIds = new TreeMap<CstFieldRef, FieldIdItem>(); + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + return fieldIds.values(); + } + + /** {@inheritDoc} */ + @Override + public IndexedItem get(Constant cst) { + if (cst == null) { + throw new NullPointerException("cst == null"); } - /** {@inheritDoc} */ - @Override - public Collection<? extends Item> items() { - return fieldIds.values(); - } - - /** {@inheritDoc} */ - @Override - public IndexedItem get(Constant cst) { - if (cst == null) { - throw new NullPointerException("cst == null"); - } - - throwIfNotPrepared(); + throwIfNotPrepared(); - IndexedItem result = fieldIds.get((CstFieldRef) cst); + IndexedItem result = fieldIds.get(cst); - if (result == null) { - throw new IllegalArgumentException("not found"); - } - - return result; + if (result == null) { + throw new IllegalArgumentException("not found"); } - /** - * Writes the portion of the file header that refers to this instance. - * - * @param out {@code non-null;} where to write - */ - public void writeHeaderPart(AnnotatedOutput out) { - throwIfNotPrepared(); + return result; + } - int sz = fieldIds.size(); - int offset = (sz == 0) ? 0 : getFileOffset(); + /** + * Writes the portion of the file header that refers to this instance. + * + * @param out {@code non-null;} where to write + */ + public void writeHeaderPart(AnnotatedOutput out) { + throwIfNotPrepared(); - if (out.annotates()) { - out.annotate(4, "field_ids_size: " + Hex.u4(sz)); - out.annotate(4, "field_ids_off: " + Hex.u4(offset)); - } + int sz = fieldIds.size(); + int offset = (sz == 0) ? 0 : getFileOffset(); - out.writeInt(sz); - out.writeInt(offset); + if (out.annotates()) { + out.annotate(4, "field_ids_size: " + Hex.u4(sz)); + out.annotate(4, "field_ids_off: " + Hex.u4(offset)); } - /** - * Interns an element into this instance. - * - * @param field {@code non-null;} the reference to intern - * @return {@code non-null;} the interned reference - */ - public FieldIdItem intern(CstFieldRef field) { - if (field == null) { - throw new NullPointerException("field == null"); - } - - throwIfPrepared(); + out.writeInt(sz); + out.writeInt(offset); + } + + /** + * Interns an element into this instance. + * + * @param field {@code non-null;} the reference to intern + * @return {@code non-null;} the interned reference + */ + public FieldIdItem intern(CstFieldRef field) { + if (field == null) { + throw new NullPointerException("field == null"); + } - FieldIdItem result = fieldIds.get(field); + throwIfPrepared(); - if (result == null) { - result = new FieldIdItem(field); - fieldIds.put(field, result); - } + FieldIdItem result = fieldIds.get(field); - return result; + if (result == null) { + result = new FieldIdItem(field); + fieldIds.put(field, result); } - /** - * Gets the index of the given reference, which must have been added - * to this instance. - * - * @param ref {@code non-null;} the reference to look up - * @return {@code >= 0;} the reference's index - */ - public int indexOf(CstFieldRef ref) { - if (ref == null) { - throw new NullPointerException("ref == null"); - } - - throwIfNotPrepared(); + return result; + } + + /** + * Gets the index of the given reference, which must have been added + * to this instance. + * + * @param ref {@code non-null;} the reference to look up + * @return {@code >= 0;} the reference's index + */ + public int indexOf(CstFieldRef ref) { + if (ref == null) { + throw new NullPointerException("ref == null"); + } - FieldIdItem item = fieldIds.get(ref); + throwIfNotPrepared(); - if (item == null) { - throw new IllegalArgumentException("not found"); - } + FieldIdItem item = fieldIds.get(ref); - return item.getIndex(); + if (item == null) { + throw new IllegalArgumentException("not found"); } + + return item.getIndex(); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/HeaderItem.java b/dx/src/com/android/jack/dx/dex/file/HeaderItem.java index 4846235..281b8b2 100644 --- a/dx/src/com/android/jack/dx/dex/file/HeaderItem.java +++ b/dx/src/com/android/jack/dx/dex/file/HeaderItem.java @@ -26,90 +26,88 @@ import com.android.jack.dx.util.Hex; * File header section of a {@code .dex} file. */ public final class HeaderItem extends IndexedItem { - /** - * Constructs an instance. - */ - public HeaderItem() { - // This space intentionally left blank. + /** + * Constructs an instance. + */ + public HeaderItem() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_HEADER_ITEM; + } + + /** {@inheritDoc} */ + @Override + public int writeSize() { + return SizeOf.HEADER_ITEM; + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + // Nothing to do here. + } + + /** {@inheritDoc} */ + @Override + public void writeTo(DexFile file, AnnotatedOutput out) { + int mapOff = file.getMap().getFileOffset(); + Section firstDataSection = file.getFirstDataSection(); + Section lastDataSection = file.getLastDataSection(); + int dataOff = firstDataSection.getFileOffset(); + int dataSize = lastDataSection.getFileOffset() + lastDataSection.writeSize() - dataOff; + + String magic = file.getDexOptions().getMagic(); + + if (out.annotates()) { + out.annotate(8, "magic: " + new CstString(magic).toQuoted()); + out.annotate(4, "checksum"); + out.annotate(20, "signature"); + out.annotate(4, "file_size: " + Hex.u4(file.getFileSize())); + out.annotate(4, "header_size: " + Hex.u4(SizeOf.HEADER_ITEM)); + out.annotate(4, "endian_tag: " + Hex.u4(DexFormat.ENDIAN_TAG)); + out.annotate(4, "link_size: 0"); + out.annotate(4, "link_off: 0"); + out.annotate(4, "map_off: " + Hex.u4(mapOff)); } - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_HEADER_ITEM; + // Write the magic number. + for (int i = 0; i < 8; i++) { + out.writeByte(magic.charAt(i)); } - /** {@inheritDoc} */ - @Override - public int writeSize() { - return SizeOf.HEADER_ITEM; - } + // Leave space for the checksum and signature. + out.writeZeroes(24); - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - // Nothing to do here. - } + out.writeInt(file.getFileSize()); + out.writeInt(SizeOf.HEADER_ITEM); + out.writeInt(DexFormat.ENDIAN_TAG); + + /* + * Write zeroes for the link size and data, as the output + * isn't a staticly linked file. + */ + out.writeZeroes(8); + + out.writeInt(mapOff); - /** {@inheritDoc} */ - @Override - public void writeTo(DexFile file, AnnotatedOutput out) { - int mapOff = file.getMap().getFileOffset(); - Section firstDataSection = file.getFirstDataSection(); - Section lastDataSection = file.getLastDataSection(); - int dataOff = firstDataSection.getFileOffset(); - int dataSize = lastDataSection.getFileOffset() + - lastDataSection.writeSize() - dataOff; - - String magic = file.getDexOptions().getMagic(); - - if (out.annotates()) { - out.annotate(8, "magic: " + new CstString(magic).toQuoted()); - out.annotate(4, "checksum"); - out.annotate(20, "signature"); - out.annotate(4, "file_size: " + - Hex.u4(file.getFileSize())); - out.annotate(4, "header_size: " + Hex.u4(SizeOf.HEADER_ITEM)); - out.annotate(4, "endian_tag: " + Hex.u4(DexFormat.ENDIAN_TAG)); - out.annotate(4, "link_size: 0"); - out.annotate(4, "link_off: 0"); - out.annotate(4, "map_off: " + Hex.u4(mapOff)); - } - - // Write the magic number. - for (int i = 0; i < 8; i++) { - out.writeByte(magic.charAt(i)); - } - - // Leave space for the checksum and signature. - out.writeZeroes(24); - - out.writeInt(file.getFileSize()); - out.writeInt(SizeOf.HEADER_ITEM); - out.writeInt(DexFormat.ENDIAN_TAG); - - /* - * Write zeroes for the link size and data, as the output - * isn't a staticly linked file. - */ - out.writeZeroes(8); - - out.writeInt(mapOff); - - // Write out each section's respective header part. - file.getStringIds().writeHeaderPart(out); - file.getTypeIds().writeHeaderPart(out); - file.getProtoIds().writeHeaderPart(out); - file.getFieldIds().writeHeaderPart(out); - file.getMethodIds().writeHeaderPart(out); - file.getClassDefs().writeHeaderPart(out); - - if (out.annotates()) { - out.annotate(4, "data_size: " + Hex.u4(dataSize)); - out.annotate(4, "data_off: " + Hex.u4(dataOff)); - } - - out.writeInt(dataSize); - out.writeInt(dataOff); + // Write out each section's respective header part. + file.getStringIds().writeHeaderPart(out); + file.getTypeIds().writeHeaderPart(out); + file.getProtoIds().writeHeaderPart(out); + file.getFieldIds().writeHeaderPart(out); + file.getMethodIds().writeHeaderPart(out); + file.getClassDefs().writeHeaderPart(out); + + if (out.annotates()) { + out.annotate(4, "data_size: " + Hex.u4(dataSize)); + out.annotate(4, "data_off: " + Hex.u4(dataOff)); } + + out.writeInt(dataSize); + out.writeInt(dataOff); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/HeaderSection.java b/dx/src/com/android/jack/dx/dex/file/HeaderSection.java index e2043e6..a5df91b 100644 --- a/dx/src/com/android/jack/dx/dex/file/HeaderSection.java +++ b/dx/src/com/android/jack/dx/dex/file/HeaderSection.java @@ -26,38 +26,38 @@ import java.util.List; * File header section of a {@code .dex} file. */ public final class HeaderSection extends UniformItemSection { - /** {@code non-null;} the list of the one item in the section */ - private final List<HeaderItem> list; - - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param file {@code non-null;} file that this instance is part of - */ - public HeaderSection(DexFile file) { - super(null, file, 4); - - HeaderItem item = new HeaderItem(); - item.setIndex(0); - - this.list = Collections.singletonList(item); - } - - /** {@inheritDoc} */ - @Override - public IndexedItem get(Constant cst) { - return null; - } - - /** {@inheritDoc} */ - @Override - public Collection<? extends Item> items() { - return list; - } - - /** {@inheritDoc} */ - @Override - protected void orderItems() { - // Nothing to do here. - } + /** {@code non-null;} the list of the one item in the section */ + private final List<HeaderItem> list; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param file {@code non-null;} file that this instance is part of + */ + public HeaderSection(DexFile file) { + super(null, file, 4); + + HeaderItem item = new HeaderItem(); + item.setIndex(0); + + this.list = Collections.singletonList(item); + } + + /** {@inheritDoc} */ + @Override + public IndexedItem get(Constant cst) { + return null; + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + return list; + } + + /** {@inheritDoc} */ + @Override + protected void orderItems() { + // Nothing to do here. + } } diff --git a/dx/src/com/android/jack/dx/dex/file/IdItem.java b/dx/src/com/android/jack/dx/dex/file/IdItem.java index acf82c4..df27a71 100644 --- a/dx/src/com/android/jack/dx/dex/file/IdItem.java +++ b/dx/src/com/android/jack/dx/dex/file/IdItem.java @@ -22,40 +22,40 @@ import com.android.jack.dx.rop.cst.CstType; * Representation of a reference to an item inside a Dalvik file. */ public abstract class IdItem extends IndexedItem { - /** - * {@code non-null;} the type constant for the defining class of - * the reference - */ - private final CstType type; + /** + * {@code non-null;} the type constant for the defining class of + * the reference + */ + private final CstType type; - /** - * Constructs an instance. - * - * @param type {@code non-null;} the type constant for the defining - * class of the reference - */ - public IdItem(CstType type) { - if (type == null) { - throw new NullPointerException("type == null"); - } - - this.type = type; + /** + * Constructs an instance. + * + * @param type {@code non-null;} the type constant for the defining + * class of the reference + */ + public IdItem(CstType type) { + if (type == null) { + throw new NullPointerException("type == null"); } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - TypeIdsSection typeIds = file.getTypeIds(); - typeIds.intern(type); - } + this.type = type; + } - /** - * Gets the type constant for the defining class of the - * reference. - * - * @return {@code non-null;} the type constant - */ - public final CstType getDefiningClass() { - return type; - } + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + TypeIdsSection typeIds = file.getTypeIds(); + typeIds.intern(type); + } + + /** + * Gets the type constant for the defining class of the + * reference. + * + * @return {@code non-null;} the type constant + */ + public final CstType getDefiningClass() { + return type; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/ImportedCodeItem.java b/dx/src/com/android/jack/dx/dex/file/ImportedCodeItem.java index c3d6fd1..0bf8a04 100644 --- a/dx/src/com/android/jack/dx/dex/file/ImportedCodeItem.java +++ b/dx/src/com/android/jack/dx/dex/file/ImportedCodeItem.java @@ -16,16 +16,13 @@ package com.android.jack.dx.dex.file; -import java.io.PrintWriter; - import com.android.jack.dx.io.Code; -import com.android.jack.dx.io.CodeReader; -import com.android.jack.dx.io.Opcodes; import com.android.jack.dx.io.Code.CatchHandler; import com.android.jack.dx.io.Code.Try; +import com.android.jack.dx.io.CodeReader; +import com.android.jack.dx.io.Opcodes; import com.android.jack.dx.io.instructions.DecodedInstruction; import com.android.jack.dx.io.instructions.ShortArrayCodeOutput; -import com.android.jack.dx.rop.cst.Constant; import com.android.jack.dx.rop.cst.CstIndexMap; import com.android.jack.dx.rop.cst.CstMethodRef; import com.android.jack.dx.util.AnnotatedOutput; @@ -33,10 +30,13 @@ import com.android.jack.dx.util.ByteArrayAnnotatedOutput; import com.android.jack.dx.util.DexException; import com.android.jack.dx.util.Hex; +import java.io.PrintWriter; + /** * Representation of all the parts needed to import methods from a {@code dex} file into another. */ -public final class ImportedCodeItem extends OffsettedItem implements com.android.jack.dx.dex.file.Code { +public final class ImportedCodeItem extends OffsettedItem implements + com.android.jack.dx.dex.file.Code { /** {@code null-ok;} the imported debug info or {@code null} if there is none; */ ImportedDebugInfoItem debugInfoItem = null; @@ -101,6 +101,7 @@ public final class ImportedCodeItem extends OffsettedItem implements com.android } /** {@inheritDoc} */ + @Override public void addContents(DexFile file) { if (debugInfoItem != null) { file.getByteData().add(debugInfoItem); @@ -132,7 +133,8 @@ public final class ImportedCodeItem extends OffsettedItem implements com.android protected void place0(Section addedTo, int offset) { int triesLength = code.getTries().length; - encodedHandlers = triesLength != 0 ? encodeAndRemapCatchHandler(addedTo.getFile()) : new byte[0]; + encodedHandlers = + triesLength != 0 ? encodeAndRemapCatchHandler(addedTo.getFile()) : new byte[0]; int catchesSize = triesLength * CatchStructs.TRY_ITEM_WRITE_SIZE + encodedHandlers.length; @@ -314,6 +316,7 @@ public final class ImportedCodeItem extends OffsettedItem implements com.android private class GenericVisitor implements CodeReader.Visitor { + @Override public void visit(DecodedInstruction[] all, DecodedInstruction decodedInst) { remappedInstructions[remappingIndex++] = decodedInst; } @@ -335,8 +338,8 @@ public final class ImportedCodeItem extends OffsettedItem implements com.android int newIndex = cstIndexMap.getRemappedCstStringIndex(file, decodedInst.getIndex()); if (decodedInst.getOpcode() != Opcodes.CONST_STRING_JUMBO && (newIndex > 0xffff)) { - throw new DexException("Cannot remap new index " + newIndex - + " into a non-jumbo instruction!"); + throw new DexException( + "Cannot remap new index " + newIndex + " into a non-jumbo instruction!"); } remappedInstructions[remappingIndex++] = decodedInst.withIndex(newIndex); @@ -356,8 +359,8 @@ public final class ImportedCodeItem extends OffsettedItem implements com.android @Override public void visit(DecodedInstruction[] all, DecodedInstruction decodedInst) { - remappedInstructions[remappingIndex++] = - decodedInst.withIndex(cstIndexMap.getRemappedCstFieldRefIndex(file, decodedInst.getIndex())); + remappedInstructions[remappingIndex++] = decodedInst.withIndex( + cstIndexMap.getRemappedCstFieldRefIndex(file, decodedInst.getIndex())); } } @@ -392,9 +395,8 @@ public final class ImportedCodeItem extends OffsettedItem implements com.android @Override public void visit(DecodedInstruction[] all, DecodedInstruction decodedInst) { - remappedInstructions[remappingIndex++] = - decodedInst.withIndex(cstIndexMap.getRemappedCstBaseMethodRefIndex(file, - decodedInst.getIndex())); + remappedInstructions[remappingIndex++] = decodedInst.withIndex( + cstIndexMap.getRemappedCstBaseMethodRefIndex(file, decodedInst.getIndex())); } } } diff --git a/dx/src/com/android/jack/dx/dex/file/ImportedDebugInfoItem.java b/dx/src/com/android/jack/dx/dex/file/ImportedDebugInfoItem.java index 1d7b4cb..4219ebb 100644 --- a/dx/src/com/android/jack/dx/dex/file/ImportedDebugInfoItem.java +++ b/dx/src/com/android/jack/dx/dex/file/ImportedDebugInfoItem.java @@ -27,13 +27,15 @@ import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_SET_PROLOGUE_E import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_START_LOCAL; import static com.android.jack.dx.dex.file.DebugInfoConstants.DBG_START_LOCAL_EXTENDED; -import java.io.PrintWriter; - import com.android.jack.dx.io.DexBuffer; -import com.android.jack.dx.rop.cst.Constant; import com.android.jack.dx.rop.cst.CstIndexMap; import com.android.jack.dx.util.AnnotatedOutput; +import java.io.PrintWriter; + +/** + * TODO(jack team) + */ public class ImportedDebugInfoItem extends OffsettedItem { /** the required alignment for instances of this class */ diff --git a/dx/src/com/android/jack/dx/dex/file/IndexedItem.java b/dx/src/com/android/jack/dx/dex/file/IndexedItem.java index 6e2c83b..9bfefec 100644 --- a/dx/src/com/android/jack/dx/dex/file/IndexedItem.java +++ b/dx/src/com/android/jack/dx/dex/file/IndexedItem.java @@ -20,62 +20,62 @@ package com.android.jack.dx.dex.file; * An item in a Dalvik file which is referenced by index. */ public abstract class IndexedItem extends Item { - /** {@code >= -1;} assigned index of the item, or {@code -1} if not - * yet assigned */ - private int index; + /** {@code >= -1;} assigned index of the item, or {@code -1} if not + * yet assigned */ + private int index; - /** - * Constructs an instance. The index is initially unassigned. - */ - public IndexedItem() { - index = -1; - } - - /** - * Gets whether or not this instance has been assigned an index. - * - * @return {@code true} iff this instance has been assigned an index - */ - public final boolean hasIndex() { - return (index >= 0); - } + /** + * Constructs an instance. The index is initially unassigned. + */ + public IndexedItem() { + index = -1; + } - /** - * Gets the item index. - * - * @return {@code >= 0;} the index - * @throws RuntimeException thrown if the item index is not yet assigned - */ - public final int getIndex() { - if (index < 0) { - throw new RuntimeException("index not yet set"); - } + /** + * Gets whether or not this instance has been assigned an index. + * + * @return {@code true} iff this instance has been assigned an index + */ + public final boolean hasIndex() { + return (index >= 0); + } - return index; + /** + * Gets the item index. + * + * @return {@code >= 0;} the index + * @throws RuntimeException thrown if the item index is not yet assigned + */ + public final int getIndex() { + if (index < 0) { + throw new RuntimeException("index not yet set"); } - /** - * Sets the item index. This method may only ever be called once - * per instance, and this will throw a {@code RuntimeException} if - * called a second (or subsequent) time. - * - * @param index {@code >= 0;} the item index - */ - public final void setIndex(int index) { - if (this.index != -1) { - throw new RuntimeException("index already set"); - } + return index; + } - this.index = index; + /** + * Sets the item index. This method may only ever be called once + * per instance, and this will throw a {@code RuntimeException} if + * called a second (or subsequent) time. + * + * @param index {@code >= 0;} the item index + */ + public final void setIndex(int index) { + if (this.index != -1) { + throw new RuntimeException("index already set"); } - /** - * Gets the index of this item as a string, suitable for including in - * annotations. - * - * @return {@code non-null;} the index string - */ - public final String indexString() { - return '[' + Integer.toHexString(index) + ']'; - } + this.index = index; + } + + /** + * Gets the index of this item as a string, suitable for including in + * annotations. + * + * @return {@code non-null;} the index string + */ + public final String indexString() { + return '[' + Integer.toHexString(index) + ']'; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/Item.java b/dx/src/com/android/jack/dx/dex/file/Item.java index 6dca0e5..f1ecd64 100644 --- a/dx/src/com/android/jack/dx/dex/file/Item.java +++ b/dx/src/com/android/jack/dx/dex/file/Item.java @@ -23,58 +23,58 @@ import com.android.jack.dx.util.AnnotatedOutput; * repeated piece of a Dalvik file. */ public abstract class Item { - /** - * Constructs an instance. - */ - public Item() { - // This space intentionally left blank. - } + /** + * Constructs an instance. + */ + public Item() { + // This space intentionally left blank. + } - /** - * Returns the item type for this instance. - * - * @return {@code non-null;} the item type - */ - public abstract ItemType itemType(); + /** + * Returns the item type for this instance. + * + * @return {@code non-null;} the item type + */ + public abstract ItemType itemType(); - /** - * Returns the human name for the particular type of item this - * instance is. - * - * @return {@code non-null;} the name - */ - public final String typeName() { - return itemType().toHuman(); - } + /** + * Returns the human name for the particular type of item this + * instance is. + * + * @return {@code non-null;} the name + */ + public final String typeName() { + return itemType().toHuman(); + } - /** - * Gets the size of this instance when written, in bytes. - * - * @return {@code >= 0;} the write size - */ - public abstract int writeSize(); + /** + * Gets the size of this instance when written, in bytes. + * + * @return {@code >= 0;} the write size + */ + public abstract int writeSize(); - /** - * Populates a {@link DexFile} with items from within this instance. - * This will <i>not</i> add an item to the file for this instance itself - * (which should have been done by whatever refers to this instance). - * - * <p><b>Note:</b> Subclasses must override this to do something - * appropriate.</p> - * - * @param file {@code non-null;} the file to populate - */ - public abstract void addContents(DexFile file); + /** + * Populates a {@link DexFile} with items from within this instance. + * This will <i>not</i> add an item to the file for this instance itself + * (which should have been done by whatever refers to this instance). + * + * <p><b>Note:</b> Subclasses must override this to do something + * appropriate.</p> + * + * @param file {@code non-null;} the file to populate + */ + public abstract void addContents(DexFile file); - /** - * Writes the representation of this instance to the given data section, - * using the given {@link DexFile} to look things up as needed. - * If this instance keeps track of its offset, then this method will - * note the written offset and will also throw an exception if this - * instance has already been written. - * - * @param file {@code non-null;} the file to use for reference - * @param out {@code non-null;} where to write to - */ - public abstract void writeTo(DexFile file, AnnotatedOutput out); + /** + * Writes the representation of this instance to the given data section, + * using the given {@link DexFile} to look things up as needed. + * If this instance keeps track of its offset, then this method will + * note the written offset and will also throw an exception if this + * instance has already been written. + * + * @param file {@code non-null;} the file to use for reference + * @param out {@code non-null;} where to write to + */ + public abstract void writeTo(DexFile file, AnnotatedOutput out); } diff --git a/dx/src/com/android/jack/dx/dex/file/ItemType.java b/dx/src/com/android/jack/dx/dex/file/ItemType.java index 6d8f274..2516994 100644 --- a/dx/src/com/android/jack/dx/dex/file/ItemType.java +++ b/dx/src/com/android/jack/dx/dex/file/ItemType.java @@ -22,76 +22,77 @@ import com.android.jack.dx.util.ToHuman; * Enumeration of all the top-level item types. */ public enum ItemType implements ToHuman { - TYPE_HEADER_ITEM( 0x0000, "header_item"), - TYPE_STRING_ID_ITEM( 0x0001, "string_id_item"), - TYPE_TYPE_ID_ITEM( 0x0002, "type_id_item"), - TYPE_PROTO_ID_ITEM( 0x0003, "proto_id_item"), - TYPE_FIELD_ID_ITEM( 0x0004, "field_id_item"), - TYPE_METHOD_ID_ITEM( 0x0005, "method_id_item"), - TYPE_CLASS_DEF_ITEM( 0x0006, "class_def_item"), - TYPE_MAP_LIST( 0x1000, "map_list"), - TYPE_TYPE_LIST( 0x1001, "type_list"), - TYPE_ANNOTATION_SET_REF_LIST( 0x1002, "annotation_set_ref_list"), - TYPE_ANNOTATION_SET_ITEM( 0x1003, "annotation_set_item"), - TYPE_CLASS_DATA_ITEM( 0x2000, "class_data_item"), - TYPE_CODE_ITEM( 0x2001, "code_item"), - TYPE_STRING_DATA_ITEM( 0x2002, "string_data_item"), - TYPE_DEBUG_INFO_ITEM( 0x2003, "debug_info_item"), - TYPE_ANNOTATION_ITEM( 0x2004, "annotation_item"), - TYPE_ENCODED_ARRAY_ITEM( 0x2005, "encoded_array_item"), - TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, "annotations_directory_item"), - TYPE_MAP_ITEM( -1, "map_item"), - TYPE_TYPE_ITEM( -1, "type_item"), - TYPE_EXCEPTION_HANDLER_ITEM( -1, "exception_handler_item"), - TYPE_ANNOTATION_SET_REF_ITEM( -1, "annotation_set_ref_item"); + TYPE_HEADER_ITEM(0x0000, "header_item"), + TYPE_STRING_ID_ITEM(0x0001, "string_id_item"), + TYPE_TYPE_ID_ITEM(0x0002, "type_id_item"), + TYPE_PROTO_ID_ITEM(0x0003, "proto_id_item"), + TYPE_FIELD_ID_ITEM(0x0004, "field_id_item"), + TYPE_METHOD_ID_ITEM(0x0005, "method_id_item"), + TYPE_CLASS_DEF_ITEM(0x0006, "class_def_item"), + TYPE_MAP_LIST(0x1000, "map_list"), + TYPE_TYPE_LIST(0x1001, "type_list"), + TYPE_ANNOTATION_SET_REF_LIST(0x1002, "annotation_set_ref_list"), + TYPE_ANNOTATION_SET_ITEM(0x1003, "annotation_set_item"), + TYPE_CLASS_DATA_ITEM(0x2000, "class_data_item"), + TYPE_CODE_ITEM(0x2001, "code_item"), + TYPE_STRING_DATA_ITEM(0x2002, "string_data_item"), + TYPE_DEBUG_INFO_ITEM(0x2003, "debug_info_item"), + TYPE_ANNOTATION_ITEM(0x2004, "annotation_item"), + TYPE_ENCODED_ARRAY_ITEM(0x2005, "encoded_array_item"), + TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, "annotations_directory_item"), + TYPE_MAP_ITEM(-1, "map_item"), + TYPE_TYPE_ITEM(-1, "type_item"), + TYPE_EXCEPTION_HANDLER_ITEM(-1, "exception_handler_item"), + TYPE_ANNOTATION_SET_REF_ITEM(-1, "annotation_set_ref_item"); - /** value when represented in a {@link MapItem} */ - private final int mapValue; + /** value when represented in a {@link MapItem} */ + private final int mapValue; - /** {@code non-null;} name of the type */ - private final String typeName; + /** {@code non-null;} name of the type */ + private final String typeName; - /** {@code non-null;} the short human name */ - private final String humanName; + /** {@code non-null;} the short human name */ + private final String humanName; - /** - * Constructs an instance. - * - * @param mapValue value when represented in a {@link MapItem} - * @param typeName {@code non-null;} name of the type - */ - private ItemType(int mapValue, String typeName) { - this.mapValue = mapValue; - this.typeName = typeName; + /** + * Constructs an instance. + * + * @param mapValue value when represented in a {@link MapItem} + * @param typeName {@code non-null;} name of the type + */ + private ItemType(int mapValue, String typeName) { + this.mapValue = mapValue; + this.typeName = typeName; - // Make the human name. - String human = typeName; - if (human.endsWith("_item")) { - human = human.substring(0, human.length() - 5); - } - this.humanName = human.replace('_', ' '); + // Make the human name. + String human = typeName; + if (human.endsWith("_item")) { + human = human.substring(0, human.length() - 5); } + this.humanName = human.replace('_', ' '); + } - /** - * Gets the map value. - * - * @return the map value - */ - public int getMapValue() { - return mapValue; - } + /** + * Gets the map value. + * + * @return the map value + */ + public int getMapValue() { + return mapValue; + } - /** - * Gets the type name. - * - * @return {@code non-null;} the type name - */ - public String getTypeName() { - return typeName; - } + /** + * Gets the type name. + * + * @return {@code non-null;} the type name + */ + public String getTypeName() { + return typeName; + } - /** {@inheritDoc} */ - public String toHuman() { - return humanName; - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return humanName; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/MapItem.java b/dx/src/com/android/jack/dx/dex/file/MapItem.java index d0bc331..21f1724 100644 --- a/dx/src/com/android/jack/dx/dex/file/MapItem.java +++ b/dx/src/com/android/jack/dx/dex/file/MapItem.java @@ -25,211 +25,188 @@ import java.util.ArrayList; * Class that represents a map item. */ public final class MapItem extends OffsettedItem { - /** file alignment of this class, in bytes */ - private static final int ALIGNMENT = 4; - - /** write size of this class, in bytes: three {@code uint}s */ - private static final int WRITE_SIZE = (4 * 3); - - /** {@code non-null;} item type this instance covers */ - private final ItemType type; - - /** {@code non-null;} section this instance covers */ - private final Section section; - - /** - * {@code null-ok;} first item covered or {@code null} if this is - * a self-reference - */ - private final Item firstItem; - - /** - * {@code null-ok;} last item covered or {@code null} if this is - * a self-reference - */ - private final Item lastItem; - - /** - * {@code > 0;} count of items covered; {@code 1} if this - * is a self-reference - */ - private final int itemCount; - - /** - * Constructs a list item with instances of this class representing - * the contents of the given array of sections, adding it to the - * given map section. - * - * @param sections {@code non-null;} the sections - * @param mapSection {@code non-null;} the section that the resulting map - * should be added to; it should be empty on entry to this method - */ - public static void addMap(Section[] sections, - MixedItemSection mapSection) { - if (sections == null) { - throw new NullPointerException("sections == null"); - } - - if (mapSection.items().size() != 0) { - throw new IllegalArgumentException( - "mapSection.items().size() != 0"); - } - - ArrayList<MapItem> items = new ArrayList<MapItem>(50); - - for (Section section : sections) { - ItemType currentType = null; - Item firstItem = null; - Item lastItem = null; - int count = 0; - - for (Item item : section.items()) { - ItemType type = item.itemType(); - if (type != currentType) { - if (count != 0) { - items.add(new MapItem(currentType, section, - firstItem, lastItem, count)); - } - currentType = type; - firstItem = item; - count = 0; - } - lastItem = item; - count++; - } - - if (count != 0) { - // Add a MapItem for the final items in the section. - items.add(new MapItem(currentType, section, - firstItem, lastItem, count)); - } else if (section == mapSection) { - // Add a MapItem for the self-referential section. - items.add(new MapItem(mapSection)); - } - } - - mapSection.add( - new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items)); + /** file alignment of this class, in bytes */ + private static final int ALIGNMENT = 4; + + /** write size of this class, in bytes: three {@code uint}s */ + private static final int WRITE_SIZE = (4 * 3); + + /** {@code non-null;} item type this instance covers */ + private final ItemType type; + + /** {@code non-null;} section this instance covers */ + private final Section section; + + /** + * {@code null-ok;} first item covered or {@code null} if this is + * a self-reference + */ + private final Item firstItem; + + /** + * {@code > 0;} count of items covered; {@code 1} if this + * is a self-reference + */ + private final int itemCount; + + /** + * Constructs a list item with instances of this class representing + * the contents of the given array of sections, adding it to the + * given map section. + * + * @param sections {@code non-null;} the sections + * @param mapSection {@code non-null;} the section that the resulting map + * should be added to; it should be empty on entry to this method + */ + public static void addMap(Section[] sections, MixedItemSection mapSection) { + if (sections == null) { + throw new NullPointerException("sections == null"); } - /** - * Constructs an instance. - * - * @param type {@code non-null;} item type this instance covers - * @param section {@code non-null;} section this instance covers - * @param firstItem {@code non-null;} first item covered - * @param lastItem {@code non-null;} last item covered - * @param itemCount {@code > 0;} count of items covered - */ - private MapItem(ItemType type, Section section, Item firstItem, - Item lastItem, int itemCount) { - super(ALIGNMENT, WRITE_SIZE); - - if (type == null) { - throw new NullPointerException("type == null"); - } - - if (section == null) { - throw new NullPointerException("section == null"); - } - - if (firstItem == null) { - throw new NullPointerException("firstItem == null"); - } - - if (lastItem == null) { - throw new NullPointerException("lastItem == null"); - } - - if (itemCount <= 0) { - throw new IllegalArgumentException("itemCount <= 0"); - } - - this.type = type; - this.section = section; - this.firstItem = firstItem; - this.lastItem = lastItem; - this.itemCount = itemCount; + if (mapSection.items().size() != 0) { + throw new IllegalArgumentException("mapSection.items().size() != 0"); } - /** - * Constructs a self-referential instance. This instance is meant to - * represent the section containing the {@code map_list}. - * - * @param section {@code non-null;} section this instance covers - */ - private MapItem(Section section) { - super(ALIGNMENT, WRITE_SIZE); - - if (section == null) { - throw new NullPointerException("section == null"); + ArrayList<MapItem> items = new ArrayList<MapItem>(50); + + for (Section section : sections) { + ItemType currentType = null; + Item firstItem = null; + int count = 0; + + for (Item item : section.items()) { + ItemType type = item.itemType(); + if (type != currentType) { + if (count != 0) { + items.add(new MapItem(currentType, section, firstItem, count)); + } + currentType = type; + firstItem = item; + count = 0; } - - this.type = ItemType.TYPE_MAP_LIST; - this.section = section; - this.firstItem = null; - this.lastItem = null; - this.itemCount = 1; + count++; + } + + if (count != 0) { + // Add a MapItem for the final items in the section. + items.add(new MapItem(currentType, section, firstItem, count)); + } else if (section == mapSection) { + // Add a MapItem for the self-referential section. + items.add(new MapItem(mapSection)); + } } - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_MAP_ITEM; + mapSection.add(new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items)); + } + + /** + * Constructs an instance. + * + * @param type {@code non-null;} item type this instance covers + * @param section {@code non-null;} section this instance covers + * @param firstItem {@code non-null;} first item covered + * @param itemCount {@code > 0;} count of items covered + */ + private MapItem(ItemType type, Section section, Item firstItem, int itemCount) { + super(ALIGNMENT, WRITE_SIZE); + + if (type == null) { + throw new NullPointerException("type == null"); } - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(100); - - sb.append(getClass().getName()); - sb.append('{'); - sb.append(section.toString()); - sb.append(' '); - sb.append(type.toHuman()); - sb.append('}'); - - return sb.toString(); + if (section == null) { + throw new NullPointerException("section == null"); } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - // We have nothing to add. + if (firstItem == null) { + throw new NullPointerException("firstItem == null"); } - /** {@inheritDoc} */ - @Override - public final String toHuman() { - return toString(); + if (itemCount <= 0) { + throw new IllegalArgumentException("itemCount <= 0"); } - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - int value = type.getMapValue(); - int offset; - - if (firstItem == null) { - offset = section.getFileOffset(); - } else { - offset = section.getAbsoluteItemOffset(firstItem); - } + this.type = type; + this.section = section; + this.firstItem = firstItem; + this.itemCount = itemCount; + } + + /** + * Constructs a self-referential instance. This instance is meant to + * represent the section containing the {@code map_list}. + * + * @param section {@code non-null;} section this instance covers + */ + private MapItem(Section section) { + super(ALIGNMENT, WRITE_SIZE); + + if (section == null) { + throw new NullPointerException("section == null"); + } - if (out.annotates()) { - out.annotate(0, offsetString() + ' ' + type.getTypeName() + - " map"); - out.annotate(2, " type: " + Hex.u2(value) + " // " + - type.toString()); - out.annotate(2, " unused: 0"); - out.annotate(4, " size: " + Hex.u4(itemCount)); - out.annotate(4, " offset: " + Hex.u4(offset)); - } + this.type = ItemType.TYPE_MAP_LIST; + this.section = section; + this.firstItem = null; + this.itemCount = 1; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_MAP_ITEM; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(100); + + sb.append(getClass().getName()); + sb.append('{'); + sb.append(section.toString()); + sb.append(' '); + sb.append(type.toHuman()); + sb.append('}'); + + return sb.toString(); + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + // We have nothing to add. + } + + /** {@inheritDoc} */ + @Override + public final String toHuman() { + return toString(); + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + int value = type.getMapValue(); + int offset; + + if (firstItem == null) { + offset = section.getFileOffset(); + } else { + offset = section.getAbsoluteItemOffset(firstItem); + } - out.writeShort(value); - out.writeShort(0); // unused - out.writeInt(itemCount); - out.writeInt(offset); + if (out.annotates()) { + out.annotate(0, offsetString() + ' ' + type.getTypeName() + " map"); + out.annotate(2, " type: " + Hex.u2(value) + " // " + type.toString()); + out.annotate(2, " unused: 0"); + out.annotate(4, " size: " + Hex.u4(itemCount)); + out.annotate(4, " offset: " + Hex.u4(offset)); } + + out.writeShort(value); + out.writeShort(0); // unused + out.writeInt(itemCount); + out.writeInt(offset); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/MemberIdItem.java b/dx/src/com/android/jack/dx/dex/file/MemberIdItem.java index bb6674a..c2011a4 100644 --- a/dx/src/com/android/jack/dx/dex/file/MemberIdItem.java +++ b/dx/src/com/android/jack/dx/dex/file/MemberIdItem.java @@ -27,83 +27,82 @@ import com.android.jack.dx.util.Hex; * Dalvik file. */ public abstract class MemberIdItem extends IdItem { - /** {@code non-null;} the constant for the member */ - private final CstMemberRef cst; + /** {@code non-null;} the constant for the member */ + private final CstMemberRef cst; - /** - * Constructs an instance. - * - * @param cst {@code non-null;} the constant for the member - */ - public MemberIdItem(CstMemberRef cst) { - super(cst.getDefiningClass()); + /** + * Constructs an instance. + * + * @param cst {@code non-null;} the constant for the member + */ + public MemberIdItem(CstMemberRef cst) { + super(cst.getDefiningClass()); - this.cst = cst; - } - - /** {@inheritDoc} */ - @Override - public int writeSize() { - return SizeOf.MEMBER_ID_ITEM; - } + this.cst = cst; + } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - super.addContents(file); + /** {@inheritDoc} */ + @Override + public int writeSize() { + return SizeOf.MEMBER_ID_ITEM; + } - StringIdsSection stringIds = file.getStringIds(); - stringIds.intern(getRef().getNat().getName()); - } + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + super.addContents(file); - /** {@inheritDoc} */ - @Override - public final void writeTo(DexFile file, AnnotatedOutput out) { - TypeIdsSection typeIds = file.getTypeIds(); - StringIdsSection stringIds = file.getStringIds(); - CstNat nat = cst.getNat(); - int classIdx = typeIds.indexOf(getDefiningClass()); - int nameIdx = stringIds.indexOf(nat.getName()); - int typoidIdx = getTypoidIdx(file); + StringIdsSection stringIds = file.getStringIds(); + stringIds.intern(getRef().getNat().getName()); + } - if (out.annotates()) { - out.annotate(0, indexString() + ' ' + cst.toHuman()); - out.annotate(2, " class_idx: " + Hex.u2(classIdx)); - out.annotate(2, String.format(" %-10s %s", getTypoidName() + ':', - Hex.u2(typoidIdx))); - out.annotate(4, " name_idx: " + Hex.u4(nameIdx)); - } + /** {@inheritDoc} */ + @Override + public final void writeTo(DexFile file, AnnotatedOutput out) { + TypeIdsSection typeIds = file.getTypeIds(); + StringIdsSection stringIds = file.getStringIds(); + CstNat nat = cst.getNat(); + int classIdx = typeIds.indexOf(getDefiningClass()); + int nameIdx = stringIds.indexOf(nat.getName()); + int typoidIdx = getTypoidIdx(file); - out.writeShort(classIdx); - out.writeShort(typoidIdx); - out.writeInt(nameIdx); + if (out.annotates()) { + out.annotate(0, indexString() + ' ' + cst.toHuman()); + out.annotate(2, " class_idx: " + Hex.u2(classIdx)); + out.annotate(2, String.format(" %-10s %s", getTypoidName() + ':', Hex.u2(typoidIdx))); + out.annotate(4, " name_idx: " + Hex.u4(nameIdx)); } - /** - * Returns the index of the type-like thing associated with - * this item, in order that it may be written out. Subclasses must - * override this to get whatever it is they need to store. - * - * @param file {@code non-null;} the file being written - * @return the index in question - */ - protected abstract int getTypoidIdx(DexFile file); + out.writeShort(classIdx); + out.writeShort(typoidIdx); + out.writeInt(nameIdx); + } - /** - * Returns the field name of the type-like thing associated with - * this item, for listing-generating purposes. Subclasses must override - * this. - * - * @return {@code non-null;} the name in question - */ - protected abstract String getTypoidName(); + /** + * Returns the index of the type-like thing associated with + * this item, in order that it may be written out. Subclasses must + * override this to get whatever it is they need to store. + * + * @param file {@code non-null;} the file being written + * @return the index in question + */ + protected abstract int getTypoidIdx(DexFile file); - /** - * Gets the member constant. - * - * @return {@code non-null;} the constant - */ - public final CstMemberRef getRef() { - return cst; - } + /** + * Returns the field name of the type-like thing associated with + * this item, for listing-generating purposes. Subclasses must override + * this. + * + * @return {@code non-null;} the name in question + */ + protected abstract String getTypoidName(); + + /** + * Gets the member constant. + * + * @return {@code non-null;} the constant + */ + public final CstMemberRef getRef() { + return cst; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/MemberIdsSection.java b/dx/src/com/android/jack/dx/dex/file/MemberIdsSection.java index 628f109..e173590 100644 --- a/dx/src/com/android/jack/dx/dex/file/MemberIdsSection.java +++ b/dx/src/com/android/jack/dx/dex/file/MemberIdsSection.java @@ -27,54 +27,59 @@ import java.util.concurrent.atomic.AtomicInteger; * Member (field or method) refs list section of a {@code .dex} file. */ public abstract class MemberIdsSection extends UniformItemSection { - /** The largest addressable member is 0xffff, in the dex spec as field@CCCC or meth@CCCC. */ - private static final int MAX_MEMBERS = 0x10000; + /** The largest addressable member is 0xffff, in the dex spec as field@CCCC or meth@CCCC. */ + private static final int MAX_MEMBERS = 0x10000; - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param name {@code null-ok;} the name of this instance, for annotation - * purposes - * @param file {@code non-null;} file that this instance is part of - */ - public MemberIdsSection(String name, DexFile file) { - super(name, file, 4); - } + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param name {@code null-ok;} the name of this instance, for annotation + * purposes + * @param file {@code non-null;} file that this instance is part of + */ + public MemberIdsSection(String name, DexFile file) { + super(name, file, 4); + } - /** {@inheritDoc} */ - @Override - protected void orderItems() { - int idx = 0; + /** {@inheritDoc} */ + @Override + protected void orderItems() { + int idx = 0; - if (items().size() > MAX_MEMBERS) { - throw new DexException(tooManyMembersMessage()); - } + if (items().size() > MAX_MEMBERS) { + throw new DexException(tooManyMembersMessage()); + } - for (Object i : items()) { - ((MemberIdItem) i).setIndex(idx); - idx++; - } + for (Object i : items()) { + ((MemberIdItem) i).setIndex(idx); + idx++; } + } - private String tooManyMembersMessage() { - Map<String, AtomicInteger> membersByPackage = new TreeMap<String, AtomicInteger>(); - for (Object member : items()) { - String packageName = ((MemberIdItem) member).getDefiningClass().getPackageName(); - AtomicInteger count = membersByPackage.get(packageName); - if (count == null) { - count = new AtomicInteger(); - membersByPackage.put(packageName, count); - } - count.incrementAndGet(); - } + private String tooManyMembersMessage() { + Map<String, AtomicInteger> membersByPackage = new TreeMap<String, AtomicInteger>(); + for (Object member : items()) { + String packageName = ((MemberIdItem) member).getDefiningClass().getPackageName(); + AtomicInteger count = membersByPackage.get(packageName); + if (count == null) { + count = new AtomicInteger(); + membersByPackage.put(packageName, count); + } + count.incrementAndGet(); + } - Formatter formatter = new Formatter(); - String memberType = this instanceof MethodIdsSection ? "methods" : "fields"; - formatter.format("Too many %s: %d; max is %d. By package:", - memberType, items().size(), MAX_MEMBERS); - for (Map.Entry<String, AtomicInteger> entry : membersByPackage.entrySet()) { - formatter.format("%n%6d %s", entry.getValue().get(), entry.getKey()); - } - return formatter.toString(); + Formatter formatter = null; + try { + formatter = new Formatter(); + String memberType = this instanceof MethodIdsSection ? "methods" : "fields"; + formatter.format("Too many %s: %d; max is %d. By package:", memberType, items().size(), + MAX_MEMBERS); + for (Map.Entry<String, AtomicInteger> entry : membersByPackage.entrySet()) { + formatter.format("%n%6d %s", entry.getValue().get(), entry.getKey()); + } + return formatter.toString(); + } finally { + formatter.close(); } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/MethodAnnotationStruct.java b/dx/src/com/android/jack/dx/dex/file/MethodAnnotationStruct.java index 8e2ca32..a12fa0f 100644 --- a/dx/src/com/android/jack/dx/dex/file/MethodAnnotationStruct.java +++ b/dx/src/com/android/jack/dx/dex/file/MethodAnnotationStruct.java @@ -25,98 +25,99 @@ import com.android.jack.dx.util.ToHuman; /** * Association of a method and its annotations. */ -public final class MethodAnnotationStruct - implements ToHuman, Comparable<MethodAnnotationStruct> { - /** {@code non-null;} the method in question */ - private final CstMethodRef method; - - /** {@code non-null;} the associated annotations */ - private AnnotationSetItem annotations; - - /** - * Constructs an instance. - * - * @param method {@code non-null;} the method in question - * @param annotations {@code non-null;} the associated annotations - */ - public MethodAnnotationStruct(CstMethodRef method, - AnnotationSetItem annotations) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - if (annotations == null) { - throw new NullPointerException("annotations == null"); - } - - this.method = method; - this.annotations = annotations; +public final class MethodAnnotationStruct implements ToHuman, Comparable<MethodAnnotationStruct> { + /** {@code non-null;} the method in question */ + private final CstMethodRef method; + + /** {@code non-null;} the associated annotations */ + private AnnotationSetItem annotations; + + /** + * Constructs an instance. + * + * @param method {@code non-null;} the method in question + * @param annotations {@code non-null;} the associated annotations + */ + public MethodAnnotationStruct(CstMethodRef method, AnnotationSetItem annotations) { + if (method == null) { + throw new NullPointerException("method == null"); } - /** {@inheritDoc} */ - public int hashCode() { - return method.hashCode(); + if (annotations == null) { + throw new NullPointerException("annotations == null"); } - /** {@inheritDoc} */ - public boolean equals(Object other) { - if (! (other instanceof MethodAnnotationStruct)) { - return false; - } - - return method.equals(((MethodAnnotationStruct) other).method); - } - - /** {@inheritDoc} */ - public int compareTo(MethodAnnotationStruct other) { - return method.compareTo(other.method); + this.method = method; + this.annotations = annotations; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return method.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof MethodAnnotationStruct)) { + return false; } - /** {@inheritDoc} */ - public void addContents(DexFile file) { - MethodIdsSection methodIds = file.getMethodIds(); - MixedItemSection wordData = file.getWordData(); - - methodIds.intern(method); - annotations = wordData.intern(annotations); + return method.equals(((MethodAnnotationStruct) other).method); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(MethodAnnotationStruct other) { + return method.compareTo(other.method); + } + + /** {@inheritDoc} */ + public void addContents(DexFile file) { + MethodIdsSection methodIds = file.getMethodIds(); + MixedItemSection wordData = file.getWordData(); + + methodIds.intern(method); + annotations = wordData.intern(annotations); + } + + /** {@inheritDoc} */ + public void writeTo(DexFile file, AnnotatedOutput out) { + int methodIdx = file.getMethodIds().indexOf(method); + int annotationsOff = annotations.getAbsoluteOffset(); + + if (out.annotates()) { + out.annotate(0, " " + method.toHuman()); + out.annotate(4, " method_idx: " + Hex.u4(methodIdx)); + out.annotate(4, " annotations_off: " + Hex.u4(annotationsOff)); } - /** {@inheritDoc} */ - public void writeTo(DexFile file, AnnotatedOutput out) { - int methodIdx = file.getMethodIds().indexOf(method); - int annotationsOff = annotations.getAbsoluteOffset(); - - if (out.annotates()) { - out.annotate(0, " " + method.toHuman()); - out.annotate(4, " method_idx: " + Hex.u4(methodIdx)); - out.annotate(4, " annotations_off: " + - Hex.u4(annotationsOff)); - } - - out.writeInt(methodIdx); - out.writeInt(annotationsOff); - } - - /** {@inheritDoc} */ - public String toHuman() { - return method.toHuman() + ": " + annotations; - } - - /** - * Gets the method this item is for. - * - * @return {@code non-null;} the method - */ - public CstMethodRef getMethod() { - return method; - } - - /** - * Gets the associated annotations. - * - * @return {@code non-null;} the annotations - */ - public Annotations getAnnotations() { - return annotations.getAnnotations(); - } + out.writeInt(methodIdx); + out.writeInt(annotationsOff); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return method.toHuman() + ": " + annotations; + } + + /** + * Gets the method this item is for. + * + * @return {@code non-null;} the method + */ + public CstMethodRef getMethod() { + return method; + } + + /** + * Gets the associated annotations. + * + * @return {@code non-null;} the annotations + */ + public Annotations getAnnotations() { + return annotations.getAnnotations(); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/MethodIdItem.java b/dx/src/com/android/jack/dx/dex/file/MethodIdItem.java index 3684172..6efc3b0 100644 --- a/dx/src/com/android/jack/dx/dex/file/MethodIdItem.java +++ b/dx/src/com/android/jack/dx/dex/file/MethodIdItem.java @@ -22,49 +22,49 @@ import com.android.jack.dx.rop.cst.CstBaseMethodRef; * Representation of a method reference inside a Dalvik file. */ public final class MethodIdItem extends MemberIdItem { - /** - * Constructs an instance. - * - * @param method {@code non-null;} the constant for the method - */ - public MethodIdItem(CstBaseMethodRef method) { - super(method); - } + /** + * Constructs an instance. + * + * @param method {@code non-null;} the constant for the method + */ + public MethodIdItem(CstBaseMethodRef method) { + super(method); + } - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_METHOD_ID_ITEM; - } + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_METHOD_ID_ITEM; + } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - super.addContents(file); + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + super.addContents(file); - ProtoIdsSection protoIds = file.getProtoIds(); - protoIds.intern(getMethodRef().getPrototype()); - } + ProtoIdsSection protoIds = file.getProtoIds(); + protoIds.intern(getMethodRef().getPrototype()); + } - /** - * Gets the method constant. - * - * @return {@code non-null;} the constant - */ - public CstBaseMethodRef getMethodRef() { - return (CstBaseMethodRef) getRef(); - } + /** + * Gets the method constant. + * + * @return {@code non-null;} the constant + */ + public CstBaseMethodRef getMethodRef() { + return (CstBaseMethodRef) getRef(); + } - /** {@inheritDoc} */ - @Override - protected int getTypoidIdx(DexFile file) { - ProtoIdsSection protoIds = file.getProtoIds(); - return protoIds.indexOf(getMethodRef().getPrototype()); - } + /** {@inheritDoc} */ + @Override + protected int getTypoidIdx(DexFile file) { + ProtoIdsSection protoIds = file.getProtoIds(); + return protoIds.indexOf(getMethodRef().getPrototype()); + } - /** {@inheritDoc} */ - @Override - protected String getTypoidName() { - return "proto_idx"; - } + /** {@inheritDoc} */ + @Override + protected String getTypoidName() { + return "proto_idx"; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/MethodIdsSection.java b/dx/src/com/android/jack/dx/dex/file/MethodIdsSection.java index 34f528e..f3dc0a6 100644 --- a/dx/src/com/android/jack/dx/dex/file/MethodIdsSection.java +++ b/dx/src/com/android/jack/dx/dex/file/MethodIdsSection.java @@ -28,110 +28,110 @@ import java.util.TreeMap; * Method refs list section of a {@code .dex} file. */ public final class MethodIdsSection extends MemberIdsSection { - /** - * {@code non-null;} map from method constants to {@link - * MethodIdItem} instances - */ - private final TreeMap<CstBaseMethodRef, MethodIdItem> methodIds; - - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param file {@code non-null;} file that this instance is part of - */ - public MethodIdsSection(DexFile file) { - super("method_ids", file); - - methodIds = new TreeMap<CstBaseMethodRef, MethodIdItem>(); + /** + * {@code non-null;} map from method constants to {@link + * MethodIdItem} instances + */ + private final TreeMap<CstBaseMethodRef, MethodIdItem> methodIds; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param file {@code non-null;} file that this instance is part of + */ + public MethodIdsSection(DexFile file) { + super("method_ids", file); + + methodIds = new TreeMap<CstBaseMethodRef, MethodIdItem>(); + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + return methodIds.values(); + } + + /** {@inheritDoc} */ + @Override + public IndexedItem get(Constant cst) { + if (cst == null) { + throw new NullPointerException("cst == null"); } - /** {@inheritDoc} */ - @Override - public Collection<? extends Item> items() { - return methodIds.values(); - } - - /** {@inheritDoc} */ - @Override - public IndexedItem get(Constant cst) { - if (cst == null) { - throw new NullPointerException("cst == null"); - } - - throwIfNotPrepared(); + throwIfNotPrepared(); - IndexedItem result = methodIds.get((CstBaseMethodRef) cst); + IndexedItem result = methodIds.get(cst); - if (result == null) { - throw new IllegalArgumentException("not found"); - } - - return result; + if (result == null) { + throw new IllegalArgumentException("not found"); } - /** - * Writes the portion of the file header that refers to this instance. - * - * @param out {@code non-null;} where to write - */ - public void writeHeaderPart(AnnotatedOutput out) { - throwIfNotPrepared(); + return result; + } - int sz = methodIds.size(); - int offset = (sz == 0) ? 0 : getFileOffset(); + /** + * Writes the portion of the file header that refers to this instance. + * + * @param out {@code non-null;} where to write + */ + public void writeHeaderPart(AnnotatedOutput out) { + throwIfNotPrepared(); - if (out.annotates()) { - out.annotate(4, "method_ids_size: " + Hex.u4(sz)); - out.annotate(4, "method_ids_off: " + Hex.u4(offset)); - } + int sz = methodIds.size(); + int offset = (sz == 0) ? 0 : getFileOffset(); - out.writeInt(sz); - out.writeInt(offset); + if (out.annotates()) { + out.annotate(4, "method_ids_size: " + Hex.u4(sz)); + out.annotate(4, "method_ids_off: " + Hex.u4(offset)); } - /** - * Interns an element into this instance. - * - * @param method {@code non-null;} the reference to intern - * @return {@code non-null;} the interned reference - */ - public MethodIdItem intern(CstBaseMethodRef method) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - throwIfPrepared(); + out.writeInt(sz); + out.writeInt(offset); + } + + /** + * Interns an element into this instance. + * + * @param method {@code non-null;} the reference to intern + * @return {@code non-null;} the interned reference + */ + public MethodIdItem intern(CstBaseMethodRef method) { + if (method == null) { + throw new NullPointerException("method == null"); + } - MethodIdItem result = methodIds.get(method); + throwIfPrepared(); - if (result == null) { - result = new MethodIdItem(method); - methodIds.put(method, result); - } + MethodIdItem result = methodIds.get(method); - return result; + if (result == null) { + result = new MethodIdItem(method); + methodIds.put(method, result); } - /** - * Gets the index of the given reference, which must have been added - * to this instance. - * - * @param ref {@code non-null;} the reference to look up - * @return {@code >= 0;} the reference's index - */ - public int indexOf(CstBaseMethodRef ref) { - if (ref == null) { - throw new NullPointerException("ref == null"); - } - - throwIfNotPrepared(); + return result; + } + + /** + * Gets the index of the given reference, which must have been added + * to this instance. + * + * @param ref {@code non-null;} the reference to look up + * @return {@code >= 0;} the reference's index + */ + public int indexOf(CstBaseMethodRef ref) { + if (ref == null) { + throw new NullPointerException("ref == null"); + } - MethodIdItem item = methodIds.get(ref); + throwIfNotPrepared(); - if (item == null) { - throw new IllegalArgumentException("not found"); - } + MethodIdItem item = methodIds.get(ref); - return item.getIndex(); + if (item == null) { + throw new IllegalArgumentException("not found"); } + + return item.getIndex(); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/MixedItemSection.java b/dx/src/com/android/jack/dx/dex/file/MixedItemSection.java index 914f78a..ad45a50 100644 --- a/dx/src/com/android/jack/dx/dex/file/MixedItemSection.java +++ b/dx/src/com/android/jack/dx/dex/file/MixedItemSection.java @@ -39,324 +39,323 @@ import java.util.TreeMap; * have a larger alignment requirement than the alignment of this instance. */ public final class MixedItemSection extends Section { - static enum SortType { - /** no sorting */ - NONE, - - /** sort by type only */ - TYPE, - - /** sort in class-major order, with instances sorted per-class */ - INSTANCE; - }; - - /** {@code non-null;} sorter which sorts instances by type */ - private static final Comparator<OffsettedItem> TYPE_SORTER = - new Comparator<OffsettedItem>() { - public int compare(OffsettedItem item1, OffsettedItem item2) { - ItemType type1 = item1.itemType(); - ItemType type2 = item2.itemType(); - return type1.compareTo(type2); - } - }; - - /** {@code non-null;} the items in this part */ - private final ArrayList<OffsettedItem> items; - - /** {@code non-null;} items that have been explicitly interned */ - private final HashMap<OffsettedItem, OffsettedItem> interns; + static enum SortType { + /** no sorting */ + NONE, - /** {@code non-null;} how to sort the items */ - private final SortType sort; + /** sort by type only */ + TYPE, - /** - * {@code >= -1;} the current size of this part, in bytes, or {@code -1} - * if not yet calculated - */ - private int writeSize; - - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param name {@code null-ok;} the name of this instance, for annotation - * purposes - * @param file {@code non-null;} file that this instance is part of - * @param alignment {@code > 0;} alignment requirement for the final output; - * must be a power of 2 - * @param sort how the items should be sorted in the final output - */ - public MixedItemSection(String name, DexFile file, int alignment, - SortType sort) { - super(name, file, alignment); - - this.items = new ArrayList<OffsettedItem>(100); - this.interns = new HashMap<OffsettedItem, OffsettedItem>(100); - this.sort = sort; - this.writeSize = -1; - } + /** sort in class-major order, with instances sorted per-class */ + INSTANCE; + }; - /** {@inheritDoc} */ + /** {@code non-null;} sorter which sorts instances by type */ + private static final Comparator<OffsettedItem> TYPE_SORTER = new Comparator<OffsettedItem>() { @Override - public Collection<? extends Item> items() { - return items; + public int compare(OffsettedItem item1, OffsettedItem item2) { + ItemType type1 = item1.itemType(); + ItemType type2 = item2.itemType(); + return type1.compareTo(type2); } - - /** {@inheritDoc} */ - @Override - public int writeSize() { - throwIfNotPrepared(); - return writeSize; + }; + + /** {@code non-null;} the items in this part */ + private final ArrayList<OffsettedItem> items; + + /** {@code non-null;} items that have been explicitly interned */ + private final HashMap<OffsettedItem, OffsettedItem> interns; + + /** {@code non-null;} how to sort the items */ + private final SortType sort; + + /** + * {@code >= -1;} the current size of this part, in bytes, or {@code -1} + * if not yet calculated + */ + private int writeSize; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param name {@code null-ok;} the name of this instance, for annotation + * purposes + * @param file {@code non-null;} file that this instance is part of + * @param alignment {@code > 0;} alignment requirement for the final output; + * must be a power of 2 + * @param sort how the items should be sorted in the final output + */ + public MixedItemSection(String name, DexFile file, int alignment, SortType sort) { + super(name, file, alignment); + + this.items = new ArrayList<OffsettedItem>(100); + this.interns = new HashMap<OffsettedItem, OffsettedItem>(100); + this.sort = sort; + this.writeSize = -1; + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + return items; + } + + /** {@inheritDoc} */ + @Override + public int writeSize() { + throwIfNotPrepared(); + return writeSize; + } + + /** {@inheritDoc} */ + @Override + public int getAbsoluteItemOffset(Item item) { + OffsettedItem oi = (OffsettedItem) item; + return oi.getAbsoluteOffset(); + } + + /** + * Gets the size of this instance, in items. + * + * @return {@code >= 0;} the size + */ + public int size() { + return items.size(); + } + + /** + * Writes the portion of the file header that refers to this instance. + * + * @param out {@code non-null;} where to write + */ + public void writeHeaderPart(AnnotatedOutput out) { + throwIfNotPrepared(); + + if (writeSize == -1) { + throw new RuntimeException("write size not yet set"); } - /** {@inheritDoc} */ - @Override - public int getAbsoluteItemOffset(Item item) { - OffsettedItem oi = (OffsettedItem) item; - return oi.getAbsoluteOffset(); - } + int sz = writeSize; + int offset = (sz == 0) ? 0 : getFileOffset(); + String name = getName(); - /** - * Gets the size of this instance, in items. - * - * @return {@code >= 0;} the size - */ - public int size() { - return items.size(); + if (name == null) { + name = "<unnamed>"; } - /** - * Writes the portion of the file header that refers to this instance. - * - * @param out {@code non-null;} where to write - */ - public void writeHeaderPart(AnnotatedOutput out) { - throwIfNotPrepared(); + int spaceCount = 15 - name.length(); + char[] spaceArr = new char[spaceCount]; + Arrays.fill(spaceArr, ' '); + String spaces = new String(spaceArr); - if (writeSize == -1) { - throw new RuntimeException("write size not yet set"); - } - - int sz = writeSize; - int offset = (sz == 0) ? 0 : getFileOffset(); - String name = getName(); - - if (name == null) { - name = "<unnamed>"; - } - - int spaceCount = 15 - name.length(); - char[] spaceArr = new char[spaceCount]; - Arrays.fill(spaceArr, ' '); - String spaces = new String(spaceArr); - - if (out.annotates()) { - out.annotate(4, name + "_size:" + spaces + Hex.u4(sz)); - out.annotate(4, name + "_off: " + spaces + Hex.u4(offset)); - } + if (out.annotates()) { + out.annotate(4, name + "_size:" + spaces + Hex.u4(sz)); + out.annotate(4, name + "_off: " + spaces + Hex.u4(offset)); + } - out.writeInt(sz); - out.writeInt(offset); + out.writeInt(sz); + out.writeInt(offset); + } + + /** + * Adds an item to this instance. This will in turn tell the given item + * that it has been added to this instance. It is invalid to add the + * same item to more than one instance, nor to add the same items + * multiple times to a single instance. + * + * @param item {@code non-null;} the item to add + */ + public void add(OffsettedItem item) { + throwIfPrepared(); + + try { + if (item.getAlignment() > getAlignment()) { + throw new IllegalArgumentException("incompatible item alignment"); + } + } catch (NullPointerException ex) { + // Elucidate the exception. + throw new NullPointerException("item == null"); } - /** - * Adds an item to this instance. This will in turn tell the given item - * that it has been added to this instance. It is invalid to add the - * same item to more than one instance, nor to add the same items - * multiple times to a single instance. - * - * @param item {@code non-null;} the item to add - */ - public void add(OffsettedItem item) { - throwIfPrepared(); - - try { - if (item.getAlignment() > getAlignment()) { - throw new IllegalArgumentException( - "incompatible item alignment"); - } - } catch (NullPointerException ex) { - // Elucidate the exception. - throw new NullPointerException("item == null"); - } + items.add(item); + } + + /** + * Interns an item in this instance, returning the interned instance + * (which may not be the one passed in). This will add the item if no + * equal item has been added. + * + * @param item {@code non-null;} the item to intern + * @return {@code non-null;} the equivalent interned instance + */ + @SuppressWarnings("unchecked") + public <T extends OffsettedItem> T intern(T item) { + throwIfPrepared(); + + OffsettedItem result = interns.get(item); + + if (result != null) { + return (T) result; + } - items.add(item); + add(item); + interns.put(item, item); + return item; + } + + /** + * Gets an item which was previously interned. + * + * @param item {@code non-null;} the item to look for + * @return {@code non-null;} the equivalent already-interned instance + */ + @SuppressWarnings("unchecked") + public <T extends OffsettedItem> T get(T item) { + throwIfNotPrepared(); + + OffsettedItem result = interns.get(item); + + if (result != null) { + return (T) result; } - /** - * Interns an item in this instance, returning the interned instance - * (which may not be the one passed in). This will add the item if no - * equal item has been added. - * - * @param item {@code non-null;} the item to intern - * @return {@code non-null;} the equivalent interned instance - */ - public <T extends OffsettedItem> T intern(T item) { - throwIfPrepared(); + throw new NoSuchElementException(item.toString()); + } + + /** + * Writes an index of contents of the items in this instance of the + * given type. If there are none, this writes nothing. If there are any, + * then the index is preceded by the given intro string. + * + * @param out {@code non-null;} where to write to + * @param itemType {@code non-null;} the item type of interest + * @param intro {@code non-null;} the introductory string for non-empty indices + */ + public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType, String intro) { + throwIfNotPrepared(); + + TreeMap<String, OffsettedItem> index = new TreeMap<String, OffsettedItem>(); + + for (OffsettedItem item : items) { + if (item.itemType() == itemType) { + String label = item.toHuman(); + index.put(label, item); + } + } - OffsettedItem result = interns.get(item); + if (index.size() == 0) { + return; + } - if (result != null) { - return (T) result; - } + out.annotate(0, intro); - add(item); - interns.put(item, item); - return item; + for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) { + String label = entry.getKey(); + OffsettedItem item = entry.getValue(); + out.annotate(0, item.offsetString() + ' ' + label + '\n'); } + } - /** - * Gets an item which was previously interned. - * - * @param item {@code non-null;} the item to look for - * @return {@code non-null;} the equivalent already-interned instance - */ - public <T extends OffsettedItem> T get(T item) { - throwIfNotPrepared(); - - OffsettedItem result = interns.get(item); + /** {@inheritDoc} */ + @Override + protected void prepare0() { + DexFile file = getFile(); - if (result != null) { - return (T) result; - } + /* + * It's okay for new items to be added as a result of an + * addContents() call; we just have to deal with the possibility. + */ - throw new NoSuchElementException(item.toString()); +int i = 0; + for (;;) { + int sz = items.size(); + if (i >= sz) { + break; + } + + for (/*i*/; i < sz; i++) { + OffsettedItem one = items.get(i); + one.addContents(file); + } + } + } + + /** + * Places all the items in this instance at particular offsets. This + * will call {@link OffsettedItem#place} on each item. If an item + * does not know its write size before the call to {@code place}, + * it is that call which is responsible for setting the write size. + * This method may only be called once per instance; subsequent calls + * will throw an exception. + */ + public void placeItems() { + throwIfNotPrepared(); + + switch (sort) { + case INSTANCE: { + Collections.sort(items); + break; + } + case TYPE: { + Collections.sort(items, TYPE_SORTER); + break; + } + default: + /* continue */ + break; } - /** - * Writes an index of contents of the items in this instance of the - * given type. If there are none, this writes nothing. If there are any, - * then the index is preceded by the given intro string. - * - * @param out {@code non-null;} where to write to - * @param itemType {@code non-null;} the item type of interest - * @param intro {@code non-null;} the introductory string for non-empty indices - */ - public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType, - String intro) { - throwIfNotPrepared(); - - TreeMap<String, OffsettedItem> index = - new TreeMap<String, OffsettedItem>(); - - for (OffsettedItem item : items) { - if (item.itemType() == itemType) { - String label = item.toHuman(); - index.put(label, item); - } - } + int sz = items.size(); + int outAt = 0; + for (int i = 0; i < sz; i++) { + OffsettedItem one = items.get(i); + try { + int placedAt = one.place(this, outAt); - if (index.size() == 0) { - return; + if (placedAt < outAt) { + throw new RuntimeException("bogus place() result for " + one); } - out.annotate(0, intro); - - for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) { - String label = entry.getKey(); - OffsettedItem item = entry.getValue(); - out.annotate(0, item.offsetString() + ' ' + label + '\n'); - } + outAt = placedAt + one.writeSize(); + } catch (RuntimeException ex) { + throw ExceptionWithContext.withContext(ex, "...while placing " + one); + } } - /** {@inheritDoc} */ - @Override - protected void prepare0() { - DexFile file = getFile(); - - /* - * It's okay for new items to be added as a result of an - * addContents() call; we just have to deal with the possibility. - */ - - int i = 0; - for (;;) { - int sz = items.size(); - if (i >= sz) { - break; - } - - for (/*i*/; i < sz; i++) { - OffsettedItem one = items.get(i); - one.addContents(file); - } + writeSize = outAt; + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(AnnotatedOutput out) { + boolean annotates = out.annotates(); + boolean first = true; + DexFile file = getFile(); + int at = 0; + + for (OffsettedItem one : items) { + if (annotates) { + if (first) { + first = false; + } else { + out.annotate(0, "\n"); } - } + } - /** - * Places all the items in this instance at particular offsets. This - * will call {@link OffsettedItem#place} on each item. If an item - * does not know its write size before the call to {@code place}, - * it is that call which is responsible for setting the write size. - * This method may only be called once per instance; subsequent calls - * will throw an exception. - */ - public void placeItems() { - throwIfNotPrepared(); - - switch (sort) { - case INSTANCE: { - Collections.sort(items); - break; - } - case TYPE: { - Collections.sort(items, TYPE_SORTER); - break; - } - } + int alignMask = one.getAlignment() - 1; + int writeAt = (at + alignMask) & ~alignMask; - int sz = items.size(); - int outAt = 0; - for (int i = 0; i < sz; i++) { - OffsettedItem one = items.get(i); - try { - int placedAt = one.place(this, outAt); - - if (placedAt < outAt) { - throw new RuntimeException("bogus place() result for " + - one); - } - - outAt = placedAt + one.writeSize(); - } catch (RuntimeException ex) { - throw ExceptionWithContext.withContext(ex, - "...while placing " + one); - } - } + if (at != writeAt) { + out.writeZeroes(writeAt - at); + at = writeAt; + } - writeSize = outAt; + one.writeTo(file, out); + at += one.writeSize(); } - /** {@inheritDoc} */ - @Override - protected void writeTo0(AnnotatedOutput out) { - boolean annotates = out.annotates(); - boolean first = true; - DexFile file = getFile(); - int at = 0; - - for (OffsettedItem one : items) { - if (annotates) { - if (first) { - first = false; - } else { - out.annotate(0, "\n"); - } - } - - int alignMask = one.getAlignment() - 1; - int writeAt = (at + alignMask) & ~alignMask; - - if (at != writeAt) { - out.writeZeroes(writeAt - at); - at = writeAt; - } - - one.writeTo(file, out); - at += one.writeSize(); - } - - if (at != writeSize) { - throw new RuntimeException("output size mismatch"); - } + if (at != writeSize) { + throw new RuntimeException("output size mismatch"); } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/OffsettedItem.java b/dx/src/com/android/jack/dx/dex/file/OffsettedItem.java index b171f68..6c294aa 100644 --- a/dx/src/com/android/jack/dx/dex/file/OffsettedItem.java +++ b/dx/src/com/android/jack/dx/dex/file/OffsettedItem.java @@ -22,293 +22,291 @@ import com.android.jack.dx.util.ExceptionWithContext; /** * An item in a Dalvik file which is referenced by absolute offset. */ -public abstract class OffsettedItem extends Item - implements Comparable<OffsettedItem> { - /** {@code > 0;} alignment requirement */ - private final int alignment; - - /** {@code >= -1;} the size of this instance when written, in bytes, or - * {@code -1} if not yet known */ - private int writeSize; - - /** - * {@code null-ok;} section the item was added to, or {@code null} if - * not yet added - */ - private Section addedTo; - - /** - * {@code >= -1;} assigned offset of the item from the start of its section, - * or {@code -1} if not yet assigned - */ - private int offset; - - /** - * Gets the absolute offset of the given item, returning {@code 0} - * if handed {@code null}. - * - * @param item {@code null-ok;} the item in question - * @return {@code >= 0;} the item's absolute offset, or {@code 0} - * if {@code item == null} - */ - public static int getAbsoluteOffsetOr0(OffsettedItem item) { - if (item == null) { - return 0; - } - - return item.getAbsoluteOffset(); +public abstract class OffsettedItem extends Item implements Comparable<OffsettedItem> { + /** {@code > 0;} alignment requirement */ + private final int alignment; + + /** {@code >= -1;} the size of this instance when written, in bytes, or + * {@code -1} if not yet known */ + private int writeSize; + + /** + * {@code null-ok;} section the item was added to, or {@code null} if + * not yet added + */ + private Section addedTo; + + /** + * {@code >= -1;} assigned offset of the item from the start of its section, + * or {@code -1} if not yet assigned + */ + private int offset; + + /** + * Gets the absolute offset of the given item, returning {@code 0} + * if handed {@code null}. + * + * @param item {@code null-ok;} the item in question + * @return {@code >= 0;} the item's absolute offset, or {@code 0} + * if {@code item == null} + */ + public static int getAbsoluteOffsetOr0(OffsettedItem item) { + if (item == null) { + return 0; } - /** - * Constructs an instance. The offset is initially unassigned. - * - * @param alignment {@code > 0;} output alignment requirement; must be a - * power of 2 - * @param writeSize {@code >= -1;} the size of this instance when written, - * in bytes, or {@code -1} if not immediately known - */ - public OffsettedItem(int alignment, int writeSize) { - Section.validateAlignment(alignment); - - if (writeSize < -1) { - throw new IllegalArgumentException("writeSize < -1"); - } - - this.alignment = alignment; - this.writeSize = writeSize; - this.addedTo = null; - this.offset = -1; + return item.getAbsoluteOffset(); + } + + /** + * Constructs an instance. The offset is initially unassigned. + * + * @param alignment {@code > 0;} output alignment requirement; must be a + * power of 2 + * @param writeSize {@code >= -1;} the size of this instance when written, + * in bytes, or {@code -1} if not immediately known + */ + public OffsettedItem(int alignment, int writeSize) { + Section.validateAlignment(alignment); + + if (writeSize < -1) { + throw new IllegalArgumentException("writeSize < -1"); } - /** - * {@inheritDoc} - * - * Comparisons for this class are defined to be type-major (if the - * types don't match then the objects are not equal), with - * {@link #compareTo0} deciding same-type comparisons. - */ - @Override - public final boolean equals(Object other) { - if (this == other) { - return true; - } - - OffsettedItem otherItem = (OffsettedItem) other; - ItemType thisType = itemType(); - ItemType otherType = otherItem.itemType(); - - if (thisType != otherType) { - return false; - } - - return (compareTo0(otherItem) == 0); + this.alignment = alignment; + this.writeSize = writeSize; + this.addedTo = null; + this.offset = -1; + } + + /** + * {@inheritDoc} + * + * Comparisons for this class are defined to be type-major (if the + * types don't match then the objects are not equal), with + * {@link #compareTo0} deciding same-type comparisons. + */ + @Override + public final boolean equals(Object other) { + if (this == other) { + return true; } - /** - * {@inheritDoc} - * - * Comparisons for this class are defined to be class-major (if the - * classes don't match then the objects are not equal), with - * {@link #compareTo0} deciding same-class comparisons. - */ - public final int compareTo(OffsettedItem other) { - if (this == other) { - return 0; - } - - ItemType thisType = itemType(); - ItemType otherType = other.itemType(); - - if (thisType != otherType) { - return thisType.compareTo(otherType); - } - - return compareTo0(other); + OffsettedItem otherItem = (OffsettedItem) other; + ItemType thisType = itemType(); + ItemType otherType = otherItem.itemType(); + + if (thisType != otherType) { + return false; + } + + return (compareTo0(otherItem) == 0); + } + + /** + * {@inheritDoc} + * + * Comparisons for this class are defined to be class-major (if the + * classes don't match then the objects are not equal), with + * {@link #compareTo0} deciding same-class comparisons. + */ + @Override + public final int compareTo(OffsettedItem other) { + if (this == other) { + return 0; } - /** - * Sets the write size of this item. This may only be called once - * per instance, and only if the size was unknown upon instance - * creation. - * - * @param writeSize {@code > 0;} the write size, in bytes - */ - public final void setWriteSize(int writeSize) { - if (writeSize < 0) { - throw new IllegalArgumentException("writeSize < 0"); - } - - if (this.writeSize >= 0) { - throw new UnsupportedOperationException("writeSize already set"); - } - - this.writeSize = writeSize; + ItemType thisType = itemType(); + ItemType otherType = other.itemType(); + + if (thisType != otherType) { + return thisType.compareTo(otherType); } - /** {@inheritDoc} - * - * @throws UnsupportedOperationException thrown if the write size - * is not yet known - */ - @Override - public final int writeSize() { - if (writeSize < 0) { - throw new UnsupportedOperationException("writeSize is unknown"); - } - - return writeSize; + return compareTo0(other); + } + + /** + * Sets the write size of this item. This may only be called once + * per instance, and only if the size was unknown upon instance + * creation. + * + * @param writeSize {@code > 0;} the write size, in bytes + */ + public final void setWriteSize(int writeSize) { + if (writeSize < 0) { + throw new IllegalArgumentException("writeSize < 0"); } - /** {@inheritDoc} */ - @Override - public final void writeTo(DexFile file, AnnotatedOutput out) { - out.alignTo(alignment); - - try { - if (writeSize < 0) { - throw new UnsupportedOperationException( - "writeSize is unknown"); - } - out.assertCursor(getAbsoluteOffset()); - } catch (RuntimeException ex) { - throw ExceptionWithContext.withContext(ex, - "...while writing " + this); - } - - writeTo0(file, out); + if (this.writeSize >= 0) { + throw new UnsupportedOperationException("writeSize already set"); } - /** - * Gets the relative item offset. The offset is from the start of - * the section which the instance was written to. - * - * @return {@code >= 0;} the offset - * @throws RuntimeException thrown if the offset is not yet known - */ - public final int getRelativeOffset() { - if (offset < 0) { - throw new RuntimeException("offset not yet known"); - } - - return offset; + this.writeSize = writeSize; + } + + /** {@inheritDoc} + * + * @throws UnsupportedOperationException thrown if the write size + * is not yet known + */ + @Override + public final int writeSize() { + if (writeSize < 0) { + throw new UnsupportedOperationException("writeSize is unknown"); } - /** - * Gets the absolute item offset. The offset is from the start of - * the file which the instance was written to. - * - * @return {@code >= 0;} the offset - * @throws RuntimeException thrown if the offset is not yet known - */ - public final int getAbsoluteOffset() { - if (offset < 0) { - throw new RuntimeException("offset not yet known"); - } - - return addedTo.getAbsoluteOffset(offset); + return writeSize; + } + + /** {@inheritDoc} */ + @Override + public final void writeTo(DexFile file, AnnotatedOutput out) { + out.alignTo(alignment); + + try { + if (writeSize < 0) { + throw new UnsupportedOperationException("writeSize is unknown"); + } + out.assertCursor(getAbsoluteOffset()); + } catch (RuntimeException ex) { + throw ExceptionWithContext.withContext(ex, "...while writing " + this); } - /** - * Indicates that this item has been added to the given section at - * the given offset. It is only valid to call this method once per - * instance. - * - * @param addedTo {@code non-null;} the section this instance has - * been added to - * @param offset {@code >= 0;} the desired offset from the start of the - * section where this instance was placed - * @return {@code >= 0;} the offset that this instance should be placed at - * in order to meet its alignment constraint - */ - public final int place(Section addedTo, int offset) { - if (addedTo == null) { - throw new NullPointerException("addedTo == null"); - } - - if (offset < 0) { - throw new IllegalArgumentException("offset < 0"); - } - - if (this.addedTo != null) { - throw new RuntimeException("already written"); - } - - int mask = alignment - 1; - offset = (offset + mask) & ~mask; - - this.addedTo = addedTo; - this.offset = offset; - - place0(addedTo, offset); - - return offset; + writeTo0(file, out); + } + + /** + * Gets the relative item offset. The offset is from the start of + * the section which the instance was written to. + * + * @return {@code >= 0;} the offset + * @throws RuntimeException thrown if the offset is not yet known + */ + public final int getRelativeOffset() { + if (offset < 0) { + throw new RuntimeException("offset not yet known"); } - /** - * Gets the alignment requirement of this instance. An instance should - * only be written when so aligned. - * - * @return {@code > 0;} the alignment requirement; must be a power of 2 - */ - public final int getAlignment() { - return alignment; + return offset; + } + + /** + * Gets the absolute item offset. The offset is from the start of + * the file which the instance was written to. + * + * @return {@code >= 0;} the offset + * @throws RuntimeException thrown if the offset is not yet known + */ + public final int getAbsoluteOffset() { + if (offset < 0) { + throw new RuntimeException("offset not yet known"); } - /** - * Gets the absolute offset of this item as a string, suitable for - * including in annotations. - * - * @return {@code non-null;} the offset string - */ - public final String offsetString() { - return '[' + Integer.toHexString(getAbsoluteOffset()) + ']'; + return addedTo.getAbsoluteOffset(offset); + } + + /** + * Indicates that this item has been added to the given section at + * the given offset. It is only valid to call this method once per + * instance. + * + * @param addedTo {@code non-null;} the section this instance has + * been added to + * @param offset {@code >= 0;} the desired offset from the start of the + * section where this instance was placed + * @return {@code >= 0;} the offset that this instance should be placed at + * in order to meet its alignment constraint + */ + public final int place(Section addedTo, int offset) { + if (addedTo == null) { + throw new NullPointerException("addedTo == null"); } - /** - * Gets a short human-readable string representing this instance. - * - * @return {@code non-null;} the human form - */ - public abstract String toHuman(); - - /** - * Compares this instance to another which is guaranteed to be of - * the same class. The default implementation of this method is to - * throw an exception (unsupported operation). If a particular - * class needs to actually sort, then it should override this - * method. - * - * @param other {@code non-null;} instance to compare to - * @return {@code -1}, {@code 0}, or {@code 1}, depending - * on the sort order of this instance and the other - */ - protected int compareTo0(OffsettedItem other) { - throw new UnsupportedOperationException("unsupported"); + if (offset < 0) { + throw new IllegalArgumentException("offset < 0"); } - /** - * Does additional work required when placing an instance. The - * default implementation of this method is a no-op. If a - * particular class needs to do something special, then it should - * override this method. In particular, if this instance did not - * know its write size up-front, then this method is responsible - * for setting it. - * - * @param addedTo {@code non-null;} the section this instance has been added to - * @param offset {@code >= 0;} the offset from the start of the - * section where this instance was placed - */ - protected void place0(Section addedTo, int offset) { - // This space intentionally left blank. + if (this.addedTo != null) { + throw new RuntimeException("already written"); } - /** - * Performs the actual write of the contents of this instance to - * the given data section. This is called by {@link #writeTo}, - * which will have taken care of ensuring alignment. - * - * @param file {@code non-null;} the file to use for reference - * @param out {@code non-null;} where to write to - */ - protected abstract void writeTo0(DexFile file, AnnotatedOutput out); + int mask = alignment - 1; + offset = (offset + mask) & ~mask; + + this.addedTo = addedTo; + this.offset = offset; + + place0(addedTo, offset); + + return offset; + } + + /** + * Gets the alignment requirement of this instance. An instance should + * only be written when so aligned. + * + * @return {@code > 0;} the alignment requirement; must be a power of 2 + */ + public final int getAlignment() { + return alignment; + } + + /** + * Gets the absolute offset of this item as a string, suitable for + * including in annotations. + * + * @return {@code non-null;} the offset string + */ + public final String offsetString() { + return '[' + Integer.toHexString(getAbsoluteOffset()) + ']'; + } + + /** + * Gets a short human-readable string representing this instance. + * + * @return {@code non-null;} the human form + */ + public abstract String toHuman(); + + /** + * Compares this instance to another which is guaranteed to be of + * the same class. The default implementation of this method is to + * throw an exception (unsupported operation). If a particular + * class needs to actually sort, then it should override this + * method. + * + * @param other {@code non-null;} instance to compare to + * @return {@code -1}, {@code 0}, or {@code 1}, depending + * on the sort order of this instance and the other + */ + protected int compareTo0(OffsettedItem other) { + throw new UnsupportedOperationException("unsupported"); + } + + /** + * Does additional work required when placing an instance. The + * default implementation of this method is a no-op. If a + * particular class needs to do something special, then it should + * override this method. In particular, if this instance did not + * know its write size up-front, then this method is responsible + * for setting it. + * + * @param addedTo {@code non-null;} the section this instance has been added to + * @param offset {@code >= 0;} the offset from the start of the + * section where this instance was placed + */ + protected void place0(Section addedTo, int offset) { + // This space intentionally left blank. + } + + /** + * Performs the actual write of the contents of this instance to + * the given data section. This is called by {@link #writeTo}, + * which will have taken care of ensuring alignment. + * + * @param file {@code non-null;} the file to use for reference + * @param out {@code non-null;} where to write to + */ + protected abstract void writeTo0(DexFile file, AnnotatedOutput out); } diff --git a/dx/src/com/android/jack/dx/dex/file/ParameterAnnotationStruct.java b/dx/src/com/android/jack/dx/dex/file/ParameterAnnotationStruct.java index 57c4db5..c8ad228 100644 --- a/dx/src/com/android/jack/dx/dex/file/ParameterAnnotationStruct.java +++ b/dx/src/com/android/jack/dx/dex/file/ParameterAnnotationStruct.java @@ -28,134 +28,135 @@ import java.util.ArrayList; /** * Association of a method and its parameter annotations. */ -public final class ParameterAnnotationStruct - implements ToHuman, Comparable<ParameterAnnotationStruct> { - /** {@code non-null;} the method in question */ - private final CstMethodRef method; - - /** {@code non-null;} the associated annotations list */ - private final AnnotationsList annotationsList; - - /** {@code non-null;} the associated annotations list, as an item */ - private final UniformListItem<AnnotationSetRefItem> annotationsItem; - - /** - * Constructs an instance. - * - * @param method {@code non-null;} the method in question - * @param annotationsList {@code non-null;} the associated annotations list - */ - public ParameterAnnotationStruct(CstMethodRef method, - AnnotationsList annotationsList) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - if (annotationsList == null) { - throw new NullPointerException("annotationsList == null"); - } - - this.method = method; - this.annotationsList = annotationsList; - - /* - * Construct an item for the annotations list. TODO: This - * requires way too much copying; fix it. - */ - - int size = annotationsList.size(); - ArrayList<AnnotationSetRefItem> arrayList = new - ArrayList<AnnotationSetRefItem>(size); - - for (int i = 0; i < size; i++) { - Annotations annotations = annotationsList.get(i); - AnnotationSetItem item = new AnnotationSetItem(annotations); - arrayList.add(new AnnotationSetRefItem(item)); - } - - this.annotationsItem = new UniformListItem<AnnotationSetRefItem>( - ItemType.TYPE_ANNOTATION_SET_REF_LIST, arrayList); +public final class ParameterAnnotationStruct implements ToHuman, + Comparable<ParameterAnnotationStruct> { + /** {@code non-null;} the method in question */ + private final CstMethodRef method; + + /** {@code non-null;} the associated annotations list */ + private final AnnotationsList annotationsList; + + /** {@code non-null;} the associated annotations list, as an item */ + private final UniformListItem<AnnotationSetRefItem> annotationsItem; + + /** + * Constructs an instance. + * + * @param method {@code non-null;} the method in question + * @param annotationsList {@code non-null;} the associated annotations list + */ + public ParameterAnnotationStruct(CstMethodRef method, AnnotationsList annotationsList) { + if (method == null) { + throw new NullPointerException("method == null"); } - /** {@inheritDoc} */ - public int hashCode() { - return method.hashCode(); + if (annotationsList == null) { + throw new NullPointerException("annotationsList == null"); } - /** {@inheritDoc} */ - public boolean equals(Object other) { - if (! (other instanceof ParameterAnnotationStruct)) { - return false; - } - - return method.equals(((ParameterAnnotationStruct) other).method); - } + this.method = method; + this.annotationsList = annotationsList; - /** {@inheritDoc} */ - public int compareTo(ParameterAnnotationStruct other) { - return method.compareTo(other.method); - } + /* + * Construct an item for the annotations list. TODO(dx team): This + * requires way too much copying; fix it. + */ - /** {@inheritDoc} */ - public void addContents(DexFile file) { - MethodIdsSection methodIds = file.getMethodIds(); - MixedItemSection wordData = file.getWordData(); +int size = annotationsList.size(); + ArrayList<AnnotationSetRefItem> arrayList = new ArrayList<AnnotationSetRefItem>(size); - methodIds.intern(method); - wordData.add(annotationsItem); + for (int i = 0; i < size; i++) { + Annotations annotations = annotationsList.get(i); + AnnotationSetItem item = new AnnotationSetItem(annotations); + arrayList.add(new AnnotationSetRefItem(item)); } - /** {@inheritDoc} */ - public void writeTo(DexFile file, AnnotatedOutput out) { - int methodIdx = file.getMethodIds().indexOf(method); - int annotationsOff = annotationsItem.getAbsoluteOffset(); - - if (out.annotates()) { - out.annotate(0, " " + method.toHuman()); - out.annotate(4, " method_idx: " + Hex.u4(methodIdx)); - out.annotate(4, " annotations_off: " + - Hex.u4(annotationsOff)); - } - - out.writeInt(methodIdx); - out.writeInt(annotationsOff); + this.annotationsItem = + new UniformListItem<AnnotationSetRefItem>(ItemType.TYPE_ANNOTATION_SET_REF_LIST, arrayList); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return method.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof ParameterAnnotationStruct)) { + return false; } - /** {@inheritDoc} */ - public String toHuman() { - StringBuilder sb = new StringBuilder(); - - sb.append(method.toHuman()); - sb.append(": "); - - boolean first = true; - for (AnnotationSetRefItem item : annotationsItem.getItems()) { - if (first) { - first = false; - } else { - sb.append(", "); - } - sb.append(item.toHuman()); - } - - return sb.toString(); + return method.equals(((ParameterAnnotationStruct) other).method); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(ParameterAnnotationStruct other) { + return method.compareTo(other.method); + } + + /** {@inheritDoc} */ + public void addContents(DexFile file) { + MethodIdsSection methodIds = file.getMethodIds(); + MixedItemSection wordData = file.getWordData(); + + methodIds.intern(method); + wordData.add(annotationsItem); + } + + /** {@inheritDoc} */ + public void writeTo(DexFile file, AnnotatedOutput out) { + int methodIdx = file.getMethodIds().indexOf(method); + int annotationsOff = annotationsItem.getAbsoluteOffset(); + + if (out.annotates()) { + out.annotate(0, " " + method.toHuman()); + out.annotate(4, " method_idx: " + Hex.u4(methodIdx)); + out.annotate(4, " annotations_off: " + Hex.u4(annotationsOff)); } - /** - * Gets the method this item is for. - * - * @return {@code non-null;} the method - */ - public CstMethodRef getMethod() { - return method; + out.writeInt(methodIdx); + out.writeInt(annotationsOff); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + StringBuilder sb = new StringBuilder(); + + sb.append(method.toHuman()); + sb.append(": "); + + boolean first = true; + for (AnnotationSetRefItem item : annotationsItem.getItems()) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append(item.toHuman()); } - /** - * Gets the associated annotations list. - * - * @return {@code non-null;} the annotations list - */ - public AnnotationsList getAnnotationsList() { - return annotationsList; - } + return sb.toString(); + } + + /** + * Gets the method this item is for. + * + * @return {@code non-null;} the method + */ + public CstMethodRef getMethod() { + return method; + } + + /** + * Gets the associated annotations list. + * + * @return {@code non-null;} the annotations list + */ + public AnnotationsList getAnnotationsList() { + return annotationsList; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/ProtoIdItem.java b/dx/src/com/android/jack/dx/dex/file/ProtoIdItem.java index 1707125..9db6c26 100644 --- a/dx/src/com/android/jack/dx/dex/file/ProtoIdItem.java +++ b/dx/src/com/android/jack/dx/dex/file/ProtoIdItem.java @@ -28,132 +28,130 @@ import com.android.jack.dx.util.Hex; * Representation of a method prototype reference inside a Dalvik file. */ public final class ProtoIdItem extends IndexedItem { - /** {@code non-null;} the wrapped prototype */ - private final Prototype prototype; - - /** {@code non-null;} the short-form of the prototype */ - private final CstString shortForm; - - /** - * {@code null-ok;} the list of parameter types or {@code null} if this - * prototype has no parameters - */ - private TypeListItem parameterTypes; - - /** - * Constructs an instance. - * - * @param prototype {@code non-null;} the constant for the prototype - */ - public ProtoIdItem(Prototype prototype) { - if (prototype == null) { - throw new NullPointerException("prototype == null"); - } - - this.prototype = prototype; - this.shortForm = makeShortForm(prototype); - - StdTypeList parameters = prototype.getParameterTypes(); - this.parameterTypes = (parameters.size() == 0) ? null - : new TypeListItem(parameters); + /** {@code non-null;} the wrapped prototype */ + private final Prototype prototype; + + /** {@code non-null;} the short-form of the prototype */ + private final CstString shortForm; + + /** + * {@code null-ok;} the list of parameter types or {@code null} if this + * prototype has no parameters + */ + private TypeListItem parameterTypes; + + /** + * Constructs an instance. + * + * @param prototype {@code non-null;} the constant for the prototype + */ + public ProtoIdItem(Prototype prototype) { + if (prototype == null) { + throw new NullPointerException("prototype == null"); } - /** - * Creates the short-form of the given prototype. - * - * @param prototype {@code non-null;} the prototype - * @return {@code non-null;} the short form - */ - private static CstString makeShortForm(Prototype prototype) { - StdTypeList parameters = prototype.getParameterTypes(); - int size = parameters.size(); - StringBuilder sb = new StringBuilder(size + 1); - - sb.append(shortFormCharFor(prototype.getReturnType())); - - for (int i = 0; i < size; i++) { - sb.append(shortFormCharFor(parameters.getType(i))); - } + this.prototype = prototype; + this.shortForm = makeShortForm(prototype); - return new CstString(sb.toString()); - } + StdTypeList parameters = prototype.getParameterTypes(); + this.parameterTypes = (parameters.size() == 0) ? null : new TypeListItem(parameters); + } - /** - * Gets the short-form character for the given type. - * - * @param type {@code non-null;} the type - * @return the corresponding short-form character - */ - private static char shortFormCharFor(Type type) { - char descriptorChar = type.getDescriptor().charAt(0); - - if (descriptorChar == '[') { - return 'L'; - } + /** + * Creates the short-form of the given prototype. + * + * @param prototype {@code non-null;} the prototype + * @return {@code non-null;} the short form + */ + private static CstString makeShortForm(Prototype prototype) { + StdTypeList parameters = prototype.getParameterTypes(); + int size = parameters.size(); + StringBuilder sb = new StringBuilder(size + 1); - return descriptorChar; - } + sb.append(shortFormCharFor(prototype.getReturnType())); - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_PROTO_ID_ITEM; + for (int i = 0; i < size; i++) { + sb.append(shortFormCharFor(parameters.getType(i))); } - /** {@inheritDoc} */ - @Override - public int writeSize() { - return SizeOf.PROTO_ID_ITEM; - } - - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - StringIdsSection stringIds = file.getStringIds(); - TypeIdsSection typeIds = file.getTypeIds(); - MixedItemSection typeLists = file.getTypeLists(); + return new CstString(sb.toString()); + } - typeIds.intern(prototype.getReturnType()); - stringIds.intern(shortForm); + /** + * Gets the short-form character for the given type. + * + * @param type {@code non-null;} the type + * @return the corresponding short-form character + */ + private static char shortFormCharFor(Type type) { + char descriptorChar = type.getDescriptor().charAt(0); - if (parameterTypes != null) { - parameterTypes = typeLists.intern(parameterTypes); - } + if (descriptorChar == '[') { + return 'L'; } - /** {@inheritDoc} */ - @Override - public void writeTo(DexFile file, AnnotatedOutput out) { - int shortyIdx = file.getStringIds().indexOf(shortForm); - int returnIdx = file.getTypeIds().indexOf(prototype.getReturnType()); - int paramsOff = OffsettedItem.getAbsoluteOffsetOr0(parameterTypes); - - if (out.annotates()) { - StringBuilder sb = new StringBuilder(); - sb.append(prototype.getReturnType().toHuman()); - sb.append(" proto("); - - StdTypeList params = prototype.getParameterTypes(); - int size = params.size(); - - for (int i = 0; i < size; i++) { - if (i != 0) { - sb.append(", "); - } - sb.append(params.getType(i).toHuman()); - } - - sb.append(")"); - out.annotate(0, indexString() + ' ' + sb.toString()); - out.annotate(4, " shorty_idx: " + Hex.u4(shortyIdx) + - " // " + shortForm.toQuoted()); - out.annotate(4, " return_type_idx: " + Hex.u4(returnIdx) + - " // " + prototype.getReturnType().toHuman()); - out.annotate(4, " parameters_off: " + Hex.u4(paramsOff)); + return descriptorChar; + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_PROTO_ID_ITEM; + } + + /** {@inheritDoc} */ + @Override + public int writeSize() { + return SizeOf.PROTO_ID_ITEM; + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + StringIdsSection stringIds = file.getStringIds(); + TypeIdsSection typeIds = file.getTypeIds(); + MixedItemSection typeLists = file.getTypeLists(); + + typeIds.intern(prototype.getReturnType()); + stringIds.intern(shortForm); + + if (parameterTypes != null) { + parameterTypes = typeLists.intern(parameterTypes); + } + } + + /** {@inheritDoc} */ + @Override + public void writeTo(DexFile file, AnnotatedOutput out) { + int shortyIdx = file.getStringIds().indexOf(shortForm); + int returnIdx = file.getTypeIds().indexOf(prototype.getReturnType()); + int paramsOff = OffsettedItem.getAbsoluteOffsetOr0(parameterTypes); + + if (out.annotates()) { + StringBuilder sb = new StringBuilder(); + sb.append(prototype.getReturnType().toHuman()); + sb.append(" proto("); + + StdTypeList params = prototype.getParameterTypes(); + int size = params.size(); + + for (int i = 0; i < size; i++) { + if (i != 0) { + sb.append(", "); } - - out.writeInt(shortyIdx); - out.writeInt(returnIdx); - out.writeInt(paramsOff); + sb.append(params.getType(i).toHuman()); + } + + sb.append(")"); + out.annotate(0, indexString() + ' ' + sb.toString()); + out.annotate(4, " shorty_idx: " + Hex.u4(shortyIdx) + " // " + shortForm.toQuoted()); + out.annotate(4, + " return_type_idx: " + Hex.u4(returnIdx) + " // " + prototype.getReturnType().toHuman()); + out.annotate(4, " parameters_off: " + Hex.u4(paramsOff)); } + + out.writeInt(shortyIdx); + out.writeInt(returnIdx); + out.writeInt(paramsOff); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/ProtoIdsSection.java b/dx/src/com/android/jack/dx/dex/file/ProtoIdsSection.java index a588d00..33cb976 100644 --- a/dx/src/com/android/jack/dx/dex/file/ProtoIdsSection.java +++ b/dx/src/com/android/jack/dx/dex/file/ProtoIdsSection.java @@ -29,112 +29,112 @@ import java.util.TreeMap; * {@code .dex} file. */ public final class ProtoIdsSection extends UniformItemSection { - /** - * {@code non-null;} map from method prototypes to {@link ProtoIdItem} instances - */ - private final TreeMap<Prototype, ProtoIdItem> protoIds; - - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param file {@code non-null;} file that this instance is part of - */ - public ProtoIdsSection(DexFile file) { - super("proto_ids", file, 4); - - protoIds = new TreeMap<Prototype, ProtoIdItem>(); + /** + * {@code non-null;} map from method prototypes to {@link ProtoIdItem} instances + */ + private final TreeMap<Prototype, ProtoIdItem> protoIds; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param file {@code non-null;} file that this instance is part of + */ + public ProtoIdsSection(DexFile file) { + super("proto_ids", file, 4); + + protoIds = new TreeMap<Prototype, ProtoIdItem>(); + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + return protoIds.values(); + } + + /** {@inheritDoc} */ + @Override + public IndexedItem get(Constant cst) { + throw new UnsupportedOperationException("unsupported"); + } + + /** + * Writes the portion of the file header that refers to this instance. + * + * @param out {@code non-null;} where to write + */ + public void writeHeaderPart(AnnotatedOutput out) { + throwIfNotPrepared(); + + int sz = protoIds.size(); + int offset = (sz == 0) ? 0 : getFileOffset(); + + if (sz > 65536) { + throw new UnsupportedOperationException("too many proto ids"); } - /** {@inheritDoc} */ - @Override - public Collection<? extends Item> items() { - return protoIds.values(); + if (out.annotates()) { + out.annotate(4, "proto_ids_size: " + Hex.u4(sz)); + out.annotate(4, "proto_ids_off: " + Hex.u4(offset)); } - /** {@inheritDoc} */ - @Override - public IndexedItem get(Constant cst) { - throw new UnsupportedOperationException("unsupported"); + out.writeInt(sz); + out.writeInt(offset); + } + + /** + * Interns an element into this instance. + * + * @param prototype {@code non-null;} the prototype to intern + * @return {@code non-null;} the interned reference + */ + public ProtoIdItem intern(Prototype prototype) { + if (prototype == null) { + throw new NullPointerException("prototype == null"); } - /** - * Writes the portion of the file header that refers to this instance. - * - * @param out {@code non-null;} where to write - */ - public void writeHeaderPart(AnnotatedOutput out) { - throwIfNotPrepared(); + throwIfPrepared(); - int sz = protoIds.size(); - int offset = (sz == 0) ? 0 : getFileOffset(); + ProtoIdItem result = protoIds.get(prototype); - if (sz > 65536) { - throw new UnsupportedOperationException("too many proto ids"); - } - - if (out.annotates()) { - out.annotate(4, "proto_ids_size: " + Hex.u4(sz)); - out.annotate(4, "proto_ids_off: " + Hex.u4(offset)); - } - - out.writeInt(sz); - out.writeInt(offset); + if (result == null) { + result = new ProtoIdItem(prototype); + protoIds.put(prototype, result); } - /** - * Interns an element into this instance. - * - * @param prototype {@code non-null;} the prototype to intern - * @return {@code non-null;} the interned reference - */ - public ProtoIdItem intern(Prototype prototype) { - if (prototype == null) { - throw new NullPointerException("prototype == null"); - } - - throwIfPrepared(); - - ProtoIdItem result = protoIds.get(prototype); - - if (result == null) { - result = new ProtoIdItem(prototype); - protoIds.put(prototype, result); - } - - return result; + return result; + } + + /** + * Gets the index of the given prototype, which must have + * been added to this instance. + * + * @param prototype {@code non-null;} the prototype to look up + * @return {@code >= 0;} the reference's index + */ + public int indexOf(Prototype prototype) { + if (prototype == null) { + throw new NullPointerException("prototype == null"); } - /** - * Gets the index of the given prototype, which must have - * been added to this instance. - * - * @param prototype {@code non-null;} the prototype to look up - * @return {@code >= 0;} the reference's index - */ - public int indexOf(Prototype prototype) { - if (prototype == null) { - throw new NullPointerException("prototype == null"); - } - - throwIfNotPrepared(); + throwIfNotPrepared(); - ProtoIdItem item = protoIds.get(prototype); + ProtoIdItem item = protoIds.get(prototype); - if (item == null) { - throw new IllegalArgumentException("not found"); - } - - return item.getIndex(); + if (item == null) { + throw new IllegalArgumentException("not found"); } - /** {@inheritDoc} */ - @Override - protected void orderItems() { - int idx = 0; + return item.getIndex(); + } + + /** {@inheritDoc} */ + @Override + protected void orderItems() { + int idx = 0; - for (Object i : items()) { - ((ProtoIdItem) i).setIndex(idx); - idx++; - } + for (Object i : items()) { + ((ProtoIdItem) i).setIndex(idx); + idx++; } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/Section.java b/dx/src/com/android/jack/dx/dex/file/Section.java index 6b86364..46d332d 100644 --- a/dx/src/com/android/jack/dx/dex/file/Section.java +++ b/dx/src/com/android/jack/dx/dex/file/Section.java @@ -25,263 +25,261 @@ import java.util.Collection; * of items of some sort or other. */ public abstract class Section { - /** {@code null-ok;} name of this part, for annotation purposes */ - private final String name; - - /** {@code non-null;} file that this instance is part of */ - private final DexFile file; - - /** {@code > 0;} alignment requirement for the final output; - * must be a power of 2 */ - private final int alignment; - - /** {@code >= -1;} offset from the start of the file to this part, or - * {@code -1} if not yet known */ - private int fileOffset; - - /** whether {@link #prepare} has been called successfully on this - * instance */ - private boolean prepared; - - /** - * Validates an alignment. - * - * @param alignment the alignment - * @throws IllegalArgumentException thrown if {@code alignment} - * isn't a positive power of 2 - */ - public static void validateAlignment(int alignment) { - if ((alignment <= 0) || - (alignment & (alignment - 1)) != 0) { - throw new IllegalArgumentException("invalid alignment"); - } + /** {@code null-ok;} name of this part, for annotation purposes */ + private final String name; + + /** {@code non-null;} file that this instance is part of */ + private final DexFile file; + + /** {@code > 0;} alignment requirement for the final output; + * must be a power of 2 */ + private final int alignment; + + /** {@code >= -1;} offset from the start of the file to this part, or + * {@code -1} if not yet known */ + private int fileOffset; + + /** whether {@link #prepare} has been called successfully on this + * instance */ + private boolean prepared; + + /** + * Validates an alignment. + * + * @param alignment the alignment + * @throws IllegalArgumentException thrown if {@code alignment} + * isn't a positive power of 2 + */ + public static void validateAlignment(int alignment) { + if ((alignment <= 0) || (alignment & (alignment - 1)) != 0) { + throw new IllegalArgumentException("invalid alignment"); } - - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param name {@code null-ok;} the name of this instance, for annotation - * purposes - * @param file {@code non-null;} file that this instance is part of - * @param alignment {@code > 0;} alignment requirement for the final output; - * must be a power of 2 - */ - public Section(String name, DexFile file, int alignment) { - if (file == null) { - throw new NullPointerException("file == null"); - } - - validateAlignment(alignment); - - this.name = name; - this.file = file; - this.alignment = alignment; - this.fileOffset = -1; - this.prepared = false; + } + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param name {@code null-ok;} the name of this instance, for annotation + * purposes + * @param file {@code non-null;} file that this instance is part of + * @param alignment {@code > 0;} alignment requirement for the final output; + * must be a power of 2 + */ + public Section(String name, DexFile file, int alignment) { + if (file == null) { + throw new NullPointerException("file == null"); } - /** - * Gets the file that this instance is part of. - * - * @return {@code non-null;} the file - */ - public final DexFile getFile() { - return file; + validateAlignment(alignment); + + this.name = name; + this.file = file; + this.alignment = alignment; + this.fileOffset = -1; + this.prepared = false; + } + + /** + * Gets the file that this instance is part of. + * + * @return {@code non-null;} the file + */ + public final DexFile getFile() { + return file; + } + + /** + * Gets the alignment for this instance's final output. + * + * @return {@code > 0;} the alignment + */ + public final int getAlignment() { + return alignment; + } + + /** + * Gets the offset from the start of the file to this part. This + * throws an exception if the offset has not yet been set. + * + * @return {@code >= 0;} the file offset + */ + public final int getFileOffset() { + if (fileOffset < 0) { + throw new RuntimeException("fileOffset not set"); } - /** - * Gets the alignment for this instance's final output. - * - * @return {@code > 0;} the alignment - */ - public final int getAlignment() { - return alignment; + return fileOffset; + } + + /** + * Sets the file offset. It is only valid to call this method once + * once per instance. + * + * @param fileOffset {@code >= 0;} the desired offset from the start of the + * file where this for this instance + * @return {@code >= 0;} the offset that this instance should be placed at + * in order to meet its alignment constraint + */ + public final int setFileOffset(int fileOffset) { + if (fileOffset < 0) { + throw new IllegalArgumentException("fileOffset < 0"); } - /** - * Gets the offset from the start of the file to this part. This - * throws an exception if the offset has not yet been set. - * - * @return {@code >= 0;} the file offset - */ - public final int getFileOffset() { - if (fileOffset < 0) { - throw new RuntimeException("fileOffset not set"); - } - - return fileOffset; + if (this.fileOffset >= 0) { + throw new RuntimeException("fileOffset already set"); } - /** - * Sets the file offset. It is only valid to call this method once - * once per instance. - * - * @param fileOffset {@code >= 0;} the desired offset from the start of the - * file where this for this instance - * @return {@code >= 0;} the offset that this instance should be placed at - * in order to meet its alignment constraint - */ - public final int setFileOffset(int fileOffset) { - if (fileOffset < 0) { - throw new IllegalArgumentException("fileOffset < 0"); - } - - if (this.fileOffset >= 0) { - throw new RuntimeException("fileOffset already set"); - } - - int mask = alignment - 1; - fileOffset = (fileOffset + mask) & ~mask; - - this.fileOffset = fileOffset; - - return fileOffset; - } + int mask = alignment - 1; + fileOffset = (fileOffset + mask) & ~mask; - /** - * Writes this instance to the given raw data object. - * - * @param out {@code non-null;} where to write to - */ - public final void writeTo(AnnotatedOutput out) { - throwIfNotPrepared(); - align(out); - - int cursor = out.getCursor(); - - if (fileOffset < 0) { - fileOffset = cursor; - } else if (fileOffset != cursor) { - throw new RuntimeException("alignment mismatch: for " + this + - ", at " + cursor + - ", but expected " + fileOffset); - } - - if (out.annotates()) { - if (name != null) { - out.annotate(0, "\n" + name + ":"); - } else if (cursor != 0) { - out.annotate(0, "\n"); - } - } - - writeTo0(out); - } + this.fileOffset = fileOffset; - /** - * Returns the absolute file offset, given an offset from the - * start of this instance's output. This is only valid to call - * once this instance has been assigned a file offset (via {@link - * #setFileOffset}). - * - * @param relative {@code >= 0;} the relative offset - * @return {@code >= 0;} the corresponding absolute file offset - */ - public final int getAbsoluteOffset(int relative) { - if (relative < 0) { - throw new IllegalArgumentException("relative < 0"); - } - - if (fileOffset < 0) { - throw new RuntimeException("fileOffset not yet set"); - } - - return fileOffset + relative; - } + return fileOffset; + } - /** - * Returns the absolute file offset of the given item which must - * be contained in this section. This is only valid to call - * once this instance has been assigned a file offset (via {@link - * #setFileOffset}). - * - * <p><b>Note:</b> Subclasses must implement this as appropriate for - * their contents.</p> - * - * @param item {@code non-null;} the item in question - * @return {@code >= 0;} the item's absolute file offset - */ - public abstract int getAbsoluteItemOffset(Item item); - - /** - * Prepares this instance for writing. This performs any necessary - * prerequisites, including particularly adding stuff to other - * sections. This method may only be called once per instance; - * subsequent calls will throw an exception. - */ - public final void prepare() { - throwIfPrepared(); - prepare0(); - prepared = true; + /** + * Writes this instance to the given raw data object. + * + * @param out {@code non-null;} where to write to + */ + public final void writeTo(AnnotatedOutput out) { + throwIfNotPrepared(); + align(out); + + int cursor = out.getCursor(); + + if (fileOffset < 0) { + fileOffset = cursor; + } else if (fileOffset != cursor) { + throw new RuntimeException( + "alignment mismatch: for " + this + ", at " + cursor + ", but expected " + fileOffset); } - /** - * Gets the collection of all the items in this section. - * It is not valid to attempt to change the returned list. - * - * @return {@code non-null;} the items - */ - public abstract Collection<? extends Item> items(); - - /** - * Does the main work of {@link #prepare}. - */ - protected abstract void prepare0(); - - /** - * Gets the size of this instance when output, in bytes. - * - * @return {@code >= 0;} the size of this instance, in bytes - */ - public abstract int writeSize(); - - /** - * Throws an exception if {@link #prepare} has not been - * called on this instance. - */ - protected final void throwIfNotPrepared() { - if (!prepared) { - throw new RuntimeException("not prepared"); - } + if (out.annotates()) { + if (name != null) { + out.annotate(0, "\n" + name + ":"); + } else if (cursor != 0) { + out.annotate(0, "\n"); + } } - /** - * Throws an exception if {@link #prepare} has already been called - * on this instance. - */ - protected final void throwIfPrepared() { - if (prepared) { - throw new RuntimeException("already prepared"); - } + writeTo0(out); + } + + /** + * Returns the absolute file offset, given an offset from the + * start of this instance's output. This is only valid to call + * once this instance has been assigned a file offset (via {@link + * #setFileOffset}). + * + * @param relative {@code >= 0;} the relative offset + * @return {@code >= 0;} the corresponding absolute file offset + */ + public final int getAbsoluteOffset(int relative) { + if (relative < 0) { + throw new IllegalArgumentException("relative < 0"); } - /** - * Aligns the output of the given data to the alignment of this instance. - * - * @param out {@code non-null;} the output to align - */ - protected final void align(AnnotatedOutput out) { - out.alignTo(alignment); + if (fileOffset < 0) { + throw new RuntimeException("fileOffset not yet set"); } - /** - * Writes this instance to the given raw data object. This gets - * called by {@link #writeTo} after aligning the cursor of - * {@code out} and verifying that either the assigned file - * offset matches the actual cursor {@code out} or that the - * file offset was not previously assigned, in which case it gets - * assigned to {@code out}'s cursor. - * - * @param out {@code non-null;} where to write to - */ - protected abstract void writeTo0(AnnotatedOutput out); - - /** - * Returns the name of this section, for annotation purposes. - * - * @return {@code null-ok;} name of this part, for annotation purposes - */ - protected final String getName() { - return name; + return fileOffset + relative; + } + + /** + * Returns the absolute file offset of the given item which must + * be contained in this section. This is only valid to call + * once this instance has been assigned a file offset (via {@link + * #setFileOffset}). + * + * <p><b>Note:</b> Subclasses must implement this as appropriate for + * their contents.</p> + * + * @param item {@code non-null;} the item in question + * @return {@code >= 0;} the item's absolute file offset + */ + public abstract int getAbsoluteItemOffset(Item item); + + /** + * Prepares this instance for writing. This performs any necessary + * prerequisites, including particularly adding stuff to other + * sections. This method may only be called once per instance; + * subsequent calls will throw an exception. + */ + public final void prepare() { + throwIfPrepared(); + prepare0(); + prepared = true; + } + + /** + * Gets the collection of all the items in this section. + * It is not valid to attempt to change the returned list. + * + * @return {@code non-null;} the items + */ + public abstract Collection<? extends Item> items(); + + /** + * Does the main work of {@link #prepare}. + */ + protected abstract void prepare0(); + + /** + * Gets the size of this instance when output, in bytes. + * + * @return {@code >= 0;} the size of this instance, in bytes + */ + public abstract int writeSize(); + + /** + * Throws an exception if {@link #prepare} has not been + * called on this instance. + */ + protected final void throwIfNotPrepared() { + if (!prepared) { + throw new RuntimeException("not prepared"); + } + } + + /** + * Throws an exception if {@link #prepare} has already been called + * on this instance. + */ + protected final void throwIfPrepared() { + if (prepared) { + throw new RuntimeException("already prepared"); } + } + + /** + * Aligns the output of the given data to the alignment of this instance. + * + * @param out {@code non-null;} the output to align + */ + protected final void align(AnnotatedOutput out) { + out.alignTo(alignment); + } + + /** + * Writes this instance to the given raw data object. This gets + * called by {@link #writeTo} after aligning the cursor of + * {@code out} and verifying that either the assigned file + * offset matches the actual cursor {@code out} or that the + * file offset was not previously assigned, in which case it gets + * assigned to {@code out}'s cursor. + * + * @param out {@code non-null;} where to write to + */ + protected abstract void writeTo0(AnnotatedOutput out); + + /** + * Returns the name of this section, for annotation purposes. + * + * @return {@code null-ok;} name of this part, for annotation purposes + */ + protected final String getName() { + return name; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/Statistics.java b/dx/src/com/android/jack/dx/dex/file/Statistics.java index c31a9d8..9e75182 100644 --- a/dx/src/com/android/jack/dx/dex/file/Statistics.java +++ b/dx/src/com/android/jack/dx/dex/file/Statistics.java @@ -26,170 +26,169 @@ import java.util.TreeMap; * Statistics about the contents of a file. */ public final class Statistics { - /** {@code non-null;} data about each type of item */ - private final HashMap<String, Data> dataMap; + /** {@code non-null;} data about each type of item */ + private final HashMap<String, Data> dataMap; + + /** + * Constructs an instance. + */ + public Statistics() { + dataMap = new HashMap<String, Data>(50); + } + + /** + * Adds the given item to the statistics. + * + * @param item {@code non-null;} the item to add + */ + public void add(Item item) { + String typeName = item.typeName(); + Data data = dataMap.get(typeName); + + if (data == null) { + dataMap.put(typeName, new Data(item, typeName)); + } else { + data.add(item); + } + } + + /** + * Adds the given list of items to the statistics. + * + * @param list {@code non-null;} the list of items to add + */ + public void addAll(Section list) { + Collection<? extends Item> items = list.items(); + for (Item item : items) { + add(item); + } + } + + /** + * Writes the statistics as an annotation. + * + * @param out {@code non-null;} where to write to + */ + public final void writeAnnotation(AnnotatedOutput out) { + if (dataMap.size() == 0) { + return; + } - /** - * Constructs an instance. - */ - public Statistics() { - dataMap = new HashMap<String, Data>(50); + out.annotate(0, "\nstatistics:\n"); + + TreeMap<String, Data> sortedData = new TreeMap<String, Data>(); + + for (Data data : dataMap.values()) { + sortedData.put(data.name, data); } - /** - * Adds the given item to the statistics. - * - * @param item {@code non-null;} the item to add - */ - public void add(Item item) { - String typeName = item.typeName(); - Data data = dataMap.get(typeName); - - if (data == null) { - dataMap.put(typeName, new Data(item, typeName)); - } else { - data.add(item); - } + for (Data data : sortedData.values()) { + data.writeAnnotation(out); + } + } + + public String toHuman() { + StringBuilder sb = new StringBuilder(); + + sb.append("Statistics:\n"); + + TreeMap<String, Data> sortedData = new TreeMap<String, Data>(); + + for (Data data : dataMap.values()) { + sortedData.put(data.name, data); + } + + for (Data data : sortedData.values()) { + sb.append(data.toHuman()); } + return sb.toString(); + } + + /** + * Statistical data about a particular class. + */ + private static class Data { + /** {@code non-null;} name to use as a label */ + private final String name; + + /** {@code >= 0;} number of instances */ + private int count; + + /** {@code >= 0;} total size of instances in bytes */ + private int totalSize; + + /** {@code >= 0;} largest size of any individual item */ + private int largestSize; + + /** {@code >= 0;} smallest size of any individual item */ + private int smallestSize; + /** - * Adds the given list of items to the statistics. + * Constructs an instance for the given item. * - * @param list {@code non-null;} the list of items to add + * @param item {@code non-null;} item in question + * @param name {@code non-null;} type name to use */ - public void addAll(Section list) { - Collection<? extends Item> items = list.items(); - for (Item item : items) { - add(item); - } + public Data(Item item, String name) { + int size = item.writeSize(); + + this.name = name; + this.count = 1; + this.totalSize = size; + this.largestSize = size; + this.smallestSize = size; } /** - * Writes the statistics as an annotation. + * Incorporates a new item. This assumes the type name matches. * - * @param out {@code non-null;} where to write to + * @param item {@code non-null;} item to incorporate */ - public final void writeAnnotation(AnnotatedOutput out) { - if (dataMap.size() == 0) { - return; - } + public void add(Item item) { + int size = item.writeSize(); - out.annotate(0, "\nstatistics:\n"); + count++; + totalSize += size; - TreeMap<String, Data> sortedData = new TreeMap<String, Data>(); + if (size > largestSize) { + largestSize = size; + } - for (Data data : dataMap.values()) { - sortedData.put(data.name, data); - } + if (size < smallestSize) { + smallestSize = size; + } + } - for (Data data : sortedData.values()) { - data.writeAnnotation(out); - } + /** + * Writes this instance as an annotation. + * + * @param out {@code non-null;} where to write to + */ + public void writeAnnotation(AnnotatedOutput out) { + out.annotate(toHuman()); } + /** + * Generates a human-readable string for this data item. + * + * @return string for human consumption. + */ public String toHuman() { - StringBuilder sb = new StringBuilder(); - - sb.append("Statistics:\n"); + StringBuilder sb = new StringBuilder(); - TreeMap<String, Data> sortedData = new TreeMap<String, Data>(); + sb.append(" " + name + ": " + count + " item" + (count == 1 ? "" : "s") + "; " + totalSize + + " bytes total\n"); - for (Data data : dataMap.values()) { - sortedData.put(data.name, data); - } + if (smallestSize == largestSize) { + sb.append(" " + smallestSize + " bytes/item\n"); + } else { + int average = totalSize / count; + sb.append( + " " + smallestSize + ".." + largestSize + " bytes/item; average " + average + "\n"); + } - for (Data data : sortedData.values()) { - sb.append(data.toHuman()); - } - - return sb.toString(); - } - - /** - * Statistical data about a particular class. - */ - private static class Data { - /** {@code non-null;} name to use as a label */ - private final String name; - - /** {@code >= 0;} number of instances */ - private int count; - - /** {@code >= 0;} total size of instances in bytes */ - private int totalSize; - - /** {@code >= 0;} largest size of any individual item */ - private int largestSize; - - /** {@code >= 0;} smallest size of any individual item */ - private int smallestSize; - - /** - * Constructs an instance for the given item. - * - * @param item {@code non-null;} item in question - * @param name {@code non-null;} type name to use - */ - public Data(Item item, String name) { - int size = item.writeSize(); - - this.name = name; - this.count = 1; - this.totalSize = size; - this.largestSize = size; - this.smallestSize = size; - } - - /** - * Incorporates a new item. This assumes the type name matches. - * - * @param item {@code non-null;} item to incorporate - */ - public void add(Item item) { - int size = item.writeSize(); - - count++; - totalSize += size; - - if (size > largestSize) { - largestSize = size; - } - - if (size < smallestSize) { - smallestSize = size; - } - } - - /** - * Writes this instance as an annotation. - * - * @param out {@code non-null;} where to write to - */ - public void writeAnnotation(AnnotatedOutput out) { - out.annotate(toHuman()); - } - - /** - * Generates a human-readable string for this data item. - * - * @return string for human consumption. - */ - public String toHuman() { - StringBuilder sb = new StringBuilder(); - - sb.append(" " + name + ": " + - count + " item" + (count == 1 ? "" : "s") + "; " + - totalSize + " bytes total\n"); - - if (smallestSize == largestSize) { - sb.append(" " + smallestSize + " bytes/item\n"); - } else { - int average = totalSize / count; - sb.append(" " + smallestSize + ".." + largestSize + - " bytes/item; average " + average + "\n"); - } - - return sb.toString(); - } + return sb.toString(); } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/StringDataItem.java b/dx/src/com/android/jack/dx/dex/file/StringDataItem.java index ee91266..bf15879 100644 --- a/dx/src/com/android/jack/dx/dex/file/StringDataItem.java +++ b/dx/src/com/android/jack/dx/dex/file/StringDataItem.java @@ -26,74 +26,72 @@ import com.android.jack.dx.util.Leb128Utils; * Representation of string data for a particular string, in a Dalvik file. */ public final class StringDataItem extends OffsettedItem { - /** {@code non-null;} the string value */ - private final CstString value; + /** {@code non-null;} the string value */ + private final CstString value; - /** - * Constructs an instance. - * - * @param value {@code non-null;} the string value - */ - public StringDataItem(CstString value) { - super(1, writeSize(value)); + /** + * Constructs an instance. + * + * @param value {@code non-null;} the string value + */ + public StringDataItem(CstString value) { + super(1, writeSize(value)); - this.value = value; - } - - /** - * Gets the write size for a given value. - * - * @param value {@code non-null;} the string value - * @return {@code >= 2}; the write size, in bytes - */ - private static int writeSize(CstString value) { - int utf16Size = value.getUtf16Size(); + this.value = value; + } - // The +1 is for the '\0' termination byte. - return Leb128Utils.unsignedLeb128Size(utf16Size) - + value.getUtf8Size() + 1; - } + /** + * Gets the write size for a given value. + * + * @param value {@code non-null;} the string value + * @return {@code >= 2}; the write size, in bytes + */ + private static int writeSize(CstString value) { + int utf16Size = value.getUtf16Size(); - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_STRING_DATA_ITEM; - } + // The +1 is for the '\0' termination byte. + return Leb128Utils.unsignedLeb128Size(utf16Size) + value.getUtf8Size() + 1; + } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - // Nothing to do here. - } + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_STRING_DATA_ITEM; + } - /** {@inheritDoc} */ - @Override - public void writeTo0(DexFile file, AnnotatedOutput out) { - ByteArray bytes = value.getBytes(); - int utf16Size = value.getUtf16Size(); + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + // Nothing to do here. + } - if (out.annotates()) { - out.annotate(Leb128Utils.unsignedLeb128Size(utf16Size), - "utf16_size: " + Hex.u4(utf16Size)); - out.annotate(bytes.size() + 1, value.toQuoted()); - } + /** {@inheritDoc} */ + @Override + public void writeTo0(DexFile file, AnnotatedOutput out) { + ByteArray bytes = value.getBytes(); + int utf16Size = value.getUtf16Size(); - out.writeUleb128(utf16Size); - out.write(bytes); - out.writeByte(0); + if (out.annotates()) { + out.annotate(Leb128Utils.unsignedLeb128Size(utf16Size), "utf16_size: " + Hex.u4(utf16Size)); + out.annotate(bytes.size() + 1, value.toQuoted()); } - /** {@inheritDoc} */ - @Override - public String toHuman() { - return value.toQuoted(); - } + out.writeUleb128(utf16Size); + out.write(bytes); + out.writeByte(0); + } - /** {@inheritDoc} */ - @Override - protected int compareTo0(OffsettedItem other) { - StringDataItem otherData = (StringDataItem) other; + /** {@inheritDoc} */ + @Override + public String toHuman() { + return value.toQuoted(); + } - return value.compareTo(otherData.value); - } + /** {@inheritDoc} */ + @Override + protected int compareTo0(OffsettedItem other) { + StringDataItem otherData = (StringDataItem) other; + + return value.compareTo(otherData.value); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/StringIdItem.java b/dx/src/com/android/jack/dx/dex/file/StringIdItem.java index ec2bc18..5d2e74a 100644 --- a/dx/src/com/android/jack/dx/dex/file/StringIdItem.java +++ b/dx/src/com/android/jack/dx/dex/file/StringIdItem.java @@ -24,103 +24,103 @@ import com.android.jack.dx.util.Hex; /** * Representation of a string inside a Dalvik file. */ -public final class StringIdItem - extends IndexedItem implements Comparable { - /** {@code non-null;} the string value */ - private final CstString value; - - /** {@code null-ok;} associated string data object, if known */ - private StringDataItem data; - - /** - * Constructs an instance. - * - * @param value {@code non-null;} the string value - */ - public StringIdItem(CstString value) { - if (value == null) { - throw new NullPointerException("value == null"); - } - - this.value = value; - this.data = null; +public final class StringIdItem extends IndexedItem implements Comparable<StringIdItem> { + /** {@code non-null;} the string value */ + private final CstString value; + + /** {@code null-ok;} associated string data object, if known */ + private StringDataItem data; + + /** + * Constructs an instance. + * + * @param value {@code non-null;} the string value + */ + public StringIdItem(CstString value) { + if (value == null) { + throw new NullPointerException("value == null"); } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof StringIdItem)) { - return false; - } + this.value = value; + this.data = null; + } - StringIdItem otherString = (StringIdItem) other; - return value.equals(otherString.value); + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof StringIdItem)) { + return false; } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return value.hashCode(); + StringIdItem otherString = (StringIdItem) other; + return value.equals(otherString.value); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return value.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(StringIdItem other) { + StringIdItem otherString = (StringIdItem) other; + return value.compareTo(otherString.value); + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_STRING_ID_ITEM; + } + + /** {@inheritDoc} */ + @Override + public int writeSize() { + return SizeOf.STRING_ID_ITEM; + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + if (data == null) { + // The string data hasn't yet been added, so add it. + MixedItemSection stringData = file.getStringData(); + data = new StringDataItem(value); + stringData.add(data); } + } - /** {@inheritDoc} */ - public int compareTo(Object other) { - StringIdItem otherString = (StringIdItem) other; - return value.compareTo(otherString.value); - } - - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_STRING_ID_ITEM; - } - - /** {@inheritDoc} */ - @Override - public int writeSize() { - return SizeOf.STRING_ID_ITEM; - } - - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - if (data == null) { - // The string data hasn't yet been added, so add it. - MixedItemSection stringData = file.getStringData(); - data = new StringDataItem(value); - stringData.add(data); - } - } - - /** {@inheritDoc} */ - @Override - public void writeTo(DexFile file, AnnotatedOutput out) { - int dataOff = data.getAbsoluteOffset(); - - if (out.annotates()) { - out.annotate(0, indexString() + ' ' + value.toQuoted(100)); - out.annotate(4, " string_data_off: " + Hex.u4(dataOff)); - } + /** {@inheritDoc} */ + @Override + public void writeTo(DexFile file, AnnotatedOutput out) { + int dataOff = data.getAbsoluteOffset(); - out.writeInt(dataOff); + if (out.annotates()) { + out.annotate(0, indexString() + ' ' + value.toQuoted(100)); + out.annotate(4, " string_data_off: " + Hex.u4(dataOff)); } - /** - * Gets the string value. - * - * @return {@code non-null;} the value - */ - public CstString getValue() { - return value; - } - - /** - * Gets the associated data object for this instance, if known. - * - * @return {@code null-ok;} the associated data object or {@code null} - * if not yet known - */ - public StringDataItem getData() { - return data; - } + out.writeInt(dataOff); + } + + /** + * Gets the string value. + * + * @return {@code non-null;} the value + */ + public CstString getValue() { + return value; + } + + /** + * Gets the associated data object for this instance, if known. + * + * @return {@code null-ok;} the associated data object or {@code null} + * if not yet known + */ + public StringDataItem getData() { + return data; + } } diff --git a/dx/src/com/android/jack/dx/dex/file/StringIdsSection.java b/dx/src/com/android/jack/dx/dex/file/StringIdsSection.java index d09563e..b9d6c0d 100644 --- a/dx/src/com/android/jack/dx/dex/file/StringIdsSection.java +++ b/dx/src/com/android/jack/dx/dex/file/StringIdsSection.java @@ -28,155 +28,154 @@ import java.util.TreeMap; /** * Strings list section of a {@code .dex} file. */ -public final class StringIdsSection - extends UniformItemSection { - /** - * {@code non-null;} map from string constants to {@link - * StringIdItem} instances - */ - private final TreeMap<CstString, StringIdItem> strings; - - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param file {@code non-null;} file that this instance is part of - */ - public StringIdsSection(DexFile file) { - super("string_ids", file, 4); - - strings = new TreeMap<CstString, StringIdItem>(); +public final class StringIdsSection extends UniformItemSection { + /** + * {@code non-null;} map from string constants to {@link + * StringIdItem} instances + */ + private final TreeMap<CstString, StringIdItem> strings; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param file {@code non-null;} file that this instance is part of + */ + public StringIdsSection(DexFile file) { + super("string_ids", file, 4); + + strings = new TreeMap<CstString, StringIdItem>(); + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + return strings.values(); + } + + /** {@inheritDoc} */ + @Override + public IndexedItem get(Constant cst) { + if (cst == null) { + throw new NullPointerException("cst == null"); } - /** {@inheritDoc} */ - @Override - public Collection<? extends Item> items() { - return strings.values(); - } - - /** {@inheritDoc} */ - @Override - public IndexedItem get(Constant cst) { - if (cst == null) { - throw new NullPointerException("cst == null"); - } - - throwIfNotPrepared(); + throwIfNotPrepared(); - IndexedItem result = strings.get((CstString) cst); + IndexedItem result = strings.get(cst); - if (result == null) { - throw new IllegalArgumentException("not found"); - } - - return result; + if (result == null) { + throw new IllegalArgumentException("not found"); } - /** - * Writes the portion of the file header that refers to this instance. - * - * @param out {@code non-null;} where to write - */ - public void writeHeaderPart(AnnotatedOutput out) { - throwIfNotPrepared(); - - int sz = strings.size(); - int offset = (sz == 0) ? 0 : getFileOffset(); + return result; + } - if (out.annotates()) { - out.annotate(4, "string_ids_size: " + Hex.u4(sz)); - out.annotate(4, "string_ids_off: " + Hex.u4(offset)); - } + /** + * Writes the portion of the file header that refers to this instance. + * + * @param out {@code non-null;} where to write + */ + public void writeHeaderPart(AnnotatedOutput out) { + throwIfNotPrepared(); - out.writeInt(sz); - out.writeInt(offset); - } + int sz = strings.size(); + int offset = (sz == 0) ? 0 : getFileOffset(); - /** - * Interns an element into this instance. - * - * @param string {@code non-null;} the string to intern, as a regular Java - * {@code String} - * @return {@code non-null;} the interned string - */ - public StringIdItem intern(String string) { - return intern(new StringIdItem(new CstString(string))); + if (out.annotates()) { + out.annotate(4, "string_ids_size: " + Hex.u4(sz)); + out.annotate(4, "string_ids_off: " + Hex.u4(offset)); } - /** - * Interns an element into this instance. - * - * @param string {@code non-null;} the string to intern, as a constant - * @return {@code non-null;} the interned string - */ - public StringIdItem intern(CstString string) { - return intern(new StringIdItem(string)); + out.writeInt(sz); + out.writeInt(offset); + } + + /** + * Interns an element into this instance. + * + * @param string {@code non-null;} the string to intern, as a regular Java + * {@code String} + * @return {@code non-null;} the interned string + */ + public StringIdItem intern(String string) { + return intern(new StringIdItem(new CstString(string))); + } + + /** + * Interns an element into this instance. + * + * @param string {@code non-null;} the string to intern, as a constant + * @return {@code non-null;} the interned string + */ + public StringIdItem intern(CstString string) { + return intern(new StringIdItem(string)); + } + + /** + * Interns an element into this instance. + * + * @param string {@code non-null;} the string to intern + * @return {@code non-null;} the interned string + */ + public StringIdItem intern(StringIdItem string) { + if (string == null) { + throw new NullPointerException("string == null"); } - /** - * Interns an element into this instance. - * - * @param string {@code non-null;} the string to intern - * @return {@code non-null;} the interned string - */ - public StringIdItem intern(StringIdItem string) { - if (string == null) { - throw new NullPointerException("string == null"); - } + throwIfPrepared(); - throwIfPrepared(); + CstString value = string.getValue(); + StringIdItem already = strings.get(value); - CstString value = string.getValue(); - StringIdItem already = strings.get(value); - - if (already != null) { - return already; - } - - strings.put(value, string); - return string; + if (already != null) { + return already; } - /** - * Interns the components of a name-and-type into this instance. - * - * @param nat {@code non-null;} the name-and-type - */ - public void intern(CstNat nat) { - intern(nat.getName()); - intern(nat.getDescriptor()); + strings.put(value, string); + return string; + } + + /** + * Interns the components of a name-and-type into this instance. + * + * @param nat {@code non-null;} the name-and-type + */ + public void intern(CstNat nat) { + intern(nat.getName()); + intern(nat.getDescriptor()); + } + + /** + * Gets the index of the given string, which must have been added + * to this instance. + * + * @param string {@code non-null;} the string to look up + * @return {@code >= 0;} the string's index + */ + public int indexOf(CstString string) { + if (string == null) { + throw new NullPointerException("string == null"); } - /** - * Gets the index of the given string, which must have been added - * to this instance. - * - * @param string {@code non-null;} the string to look up - * @return {@code >= 0;} the string's index - */ - public int indexOf(CstString string) { - if (string == null) { - throw new NullPointerException("string == null"); - } - - throwIfNotPrepared(); + throwIfNotPrepared(); - StringIdItem s = strings.get(string); + StringIdItem s = strings.get(string); - if (s == null) { - throw new IllegalArgumentException("not found"); - } - - return s.getIndex(); + if (s == null) { + throw new IllegalArgumentException("not found"); } - /** {@inheritDoc} */ - @Override - protected void orderItems() { - int idx = 0; + return s.getIndex(); + } + + /** {@inheritDoc} */ + @Override + protected void orderItems() { + int idx = 0; - for (StringIdItem s : strings.values()) { - s.setIndex(idx); - idx++; - } + for (StringIdItem s : strings.values()) { + s.setIndex(idx); + idx++; } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/TypeIdItem.java b/dx/src/com/android/jack/dx/dex/file/TypeIdItem.java index cdbfb44..417afb5 100644 --- a/dx/src/com/android/jack/dx/dex/file/TypeIdItem.java +++ b/dx/src/com/android/jack/dx/dex/file/TypeIdItem.java @@ -26,45 +26,45 @@ import com.android.jack.dx.util.Hex; * Representation of a type reference inside a Dalvik file. */ public final class TypeIdItem extends IdItem { - /** - * Constructs an instance. - * - * @param type {@code non-null;} the constant for the type - */ - public TypeIdItem(CstType type) { - super(type); - } - - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_TYPE_ID_ITEM; - } + /** + * Constructs an instance. + * + * @param type {@code non-null;} the constant for the type + */ + public TypeIdItem(CstType type) { + super(type); + } - /** {@inheritDoc} */ - @Override - public int writeSize() { - return SizeOf.TYPE_ID_ITEM; - } + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_TYPE_ID_ITEM; + } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - file.getStringIds().intern(getDefiningClass().getDescriptor()); - } + /** {@inheritDoc} */ + @Override + public int writeSize() { + return SizeOf.TYPE_ID_ITEM; + } - /** {@inheritDoc} */ - @Override - public void writeTo(DexFile file, AnnotatedOutput out) { - CstType type = getDefiningClass(); - CstString descriptor = type.getDescriptor(); - int idx = file.getStringIds().indexOf(descriptor); + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + file.getStringIds().intern(getDefiningClass().getDescriptor()); + } - if (out.annotates()) { - out.annotate(0, indexString() + ' ' + descriptor.toHuman()); - out.annotate(4, " descriptor_idx: " + Hex.u4(idx)); - } + /** {@inheritDoc} */ + @Override + public void writeTo(DexFile file, AnnotatedOutput out) { + CstType type = getDefiningClass(); + CstString descriptor = type.getDescriptor(); + int idx = file.getStringIds().indexOf(descriptor); - out.writeInt(idx); + if (out.annotates()) { + out.annotate(0, indexString() + ' ' + descriptor.toHuman()); + out.annotate(4, " descriptor_idx: " + Hex.u4(idx)); } + + out.writeInt(idx); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/TypeIdsSection.java b/dx/src/com/android/jack/dx/dex/file/TypeIdsSection.java index fede94e..34599c0 100644 --- a/dx/src/com/android/jack/dx/dex/file/TypeIdsSection.java +++ b/dx/src/com/android/jack/dx/dex/file/TypeIdsSection.java @@ -29,164 +29,164 @@ import java.util.TreeMap; * Type identifiers list section of a {@code .dex} file. */ public final class TypeIdsSection extends UniformItemSection { - /** - * {@code non-null;} map from types to {@link TypeIdItem} instances - */ - private final TreeMap<Type, TypeIdItem> typeIds; - - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param file {@code non-null;} file that this instance is part of - */ - public TypeIdsSection(DexFile file) { - super("type_ids", file, 4); - - typeIds = new TreeMap<Type, TypeIdItem>(); + /** + * {@code non-null;} map from types to {@link TypeIdItem} instances + */ + private final TreeMap<Type, TypeIdItem> typeIds; + + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param file {@code non-null;} file that this instance is part of + */ + public TypeIdsSection(DexFile file) { + super("type_ids", file, 4); + + typeIds = new TreeMap<Type, TypeIdItem>(); + } + + /** {@inheritDoc} */ + @Override + public Collection<? extends Item> items() { + return typeIds.values(); + } + + /** {@inheritDoc} */ + @Override + public IndexedItem get(Constant cst) { + if (cst == null) { + throw new NullPointerException("cst == null"); } - /** {@inheritDoc} */ - @Override - public Collection<? extends Item> items() { - return typeIds.values(); - } - - /** {@inheritDoc} */ - @Override - public IndexedItem get(Constant cst) { - if (cst == null) { - throw new NullPointerException("cst == null"); - } - - throwIfNotPrepared(); + throwIfNotPrepared(); - Type type = ((CstType) cst).getClassType(); - IndexedItem result = typeIds.get(type); + Type type = ((CstType) cst).getClassType(); + IndexedItem result = typeIds.get(type); - if (result == null) { - throw new IllegalArgumentException("not found: " + cst); - } - - return result; + if (result == null) { + throw new IllegalArgumentException("not found: " + cst); } - /** - * Writes the portion of the file header that refers to this instance. - * - * @param out {@code non-null;} where to write - */ - public void writeHeaderPart(AnnotatedOutput out) { - throwIfNotPrepared(); - - int sz = typeIds.size(); - int offset = (sz == 0) ? 0 : getFileOffset(); + return result; + } - if (sz > 65536) { - throw new UnsupportedOperationException("too many type ids"); - } + /** + * Writes the portion of the file header that refers to this instance. + * + * @param out {@code non-null;} where to write + */ + public void writeHeaderPart(AnnotatedOutput out) { + throwIfNotPrepared(); - if (out.annotates()) { - out.annotate(4, "type_ids_size: " + Hex.u4(sz)); - out.annotate(4, "type_ids_off: " + Hex.u4(offset)); - } + int sz = typeIds.size(); + int offset = (sz == 0) ? 0 : getFileOffset(); - out.writeInt(sz); - out.writeInt(offset); + if (sz > 65536) { + throw new UnsupportedOperationException("too many type ids"); } - /** - * Interns an element into this instance. - * - * @param type {@code non-null;} the type to intern - * @return {@code non-null;} the interned reference - */ - public TypeIdItem intern(Type type) { - if (type == null) { - throw new NullPointerException("type == null"); - } + if (out.annotates()) { + out.annotate(4, "type_ids_size: " + Hex.u4(sz)); + out.annotate(4, "type_ids_off: " + Hex.u4(offset)); + } - throwIfPrepared(); + out.writeInt(sz); + out.writeInt(offset); + } + + /** + * Interns an element into this instance. + * + * @param type {@code non-null;} the type to intern + * @return {@code non-null;} the interned reference + */ + public TypeIdItem intern(Type type) { + if (type == null) { + throw new NullPointerException("type == null"); + } - TypeIdItem result = typeIds.get(type); + throwIfPrepared(); - if (result == null) { - result = new TypeIdItem(new CstType(type)); - typeIds.put(type, result); - } + TypeIdItem result = typeIds.get(type); - return result; + if (result == null) { + result = new TypeIdItem(new CstType(type)); + typeIds.put(type, result); } - /** - * Interns an element into this instance. - * - * @param type {@code non-null;} the type to intern - * @return {@code non-null;} the interned reference - */ - public TypeIdItem intern(CstType type) { - if (type == null) { - throw new NullPointerException("type == null"); - } - - throwIfPrepared(); + return result; + } + + /** + * Interns an element into this instance. + * + * @param type {@code non-null;} the type to intern + * @return {@code non-null;} the interned reference + */ + public TypeIdItem intern(CstType type) { + if (type == null) { + throw new NullPointerException("type == null"); + } - Type typePerSe = type.getClassType(); - TypeIdItem result = typeIds.get(typePerSe); + throwIfPrepared(); - if (result == null) { - result = new TypeIdItem(type); - typeIds.put(typePerSe, result); - } + Type typePerSe = type.getClassType(); + TypeIdItem result = typeIds.get(typePerSe); - return result; + if (result == null) { + result = new TypeIdItem(type); + typeIds.put(typePerSe, result); } - /** - * Gets the index of the given type, which must have - * been added to this instance. - * - * @param type {@code non-null;} the type to look up - * @return {@code >= 0;} the reference's index - */ - public int indexOf(Type type) { - if (type == null) { - throw new NullPointerException("type == null"); - } - - throwIfNotPrepared(); + return result; + } + + /** + * Gets the index of the given type, which must have + * been added to this instance. + * + * @param type {@code non-null;} the type to look up + * @return {@code >= 0;} the reference's index + */ + public int indexOf(Type type) { + if (type == null) { + throw new NullPointerException("type == null"); + } - TypeIdItem item = typeIds.get(type); + throwIfNotPrepared(); - if (item == null) { - throw new IllegalArgumentException("not found: " + type); - } + TypeIdItem item = typeIds.get(type); - return item.getIndex(); + if (item == null) { + throw new IllegalArgumentException("not found: " + type); } - /** - * Gets the index of the given type, which must have - * been added to this instance. - * - * @param type {@code non-null;} the type to look up - * @return {@code >= 0;} the reference's index - */ - public int indexOf(CstType type) { - if (type == null) { - throw new NullPointerException("type == null"); - } - - return indexOf(type.getClassType()); + return item.getIndex(); + } + + /** + * Gets the index of the given type, which must have + * been added to this instance. + * + * @param type {@code non-null;} the type to look up + * @return {@code >= 0;} the reference's index + */ + public int indexOf(CstType type) { + if (type == null) { + throw new NullPointerException("type == null"); } - /** {@inheritDoc} */ - @Override - protected void orderItems() { - int idx = 0; + return indexOf(type.getClassType()); + } + + /** {@inheritDoc} */ + @Override + protected void orderItems() { + int idx = 0; - for (Object i : items()) { - ((TypeIdItem) i).setIndex(idx); - idx++; - } + for (Object i : items()) { + ((TypeIdItem) i).setIndex(idx); + idx++; } + } } diff --git a/dx/src/com/android/jack/dx/dex/file/TypeListItem.java b/dx/src/com/android/jack/dx/dex/file/TypeListItem.java index 42c1c5a..263d2b4 100644 --- a/dx/src/com/android/jack/dx/dex/file/TypeListItem.java +++ b/dx/src/com/android/jack/dx/dex/file/TypeListItem.java @@ -16,7 +16,6 @@ package com.android.jack.dx.dex.file; -import com.android.jack.dx.rop.cst.CstType; import com.android.jack.dx.rop.type.StdTypeList; import com.android.jack.dx.rop.type.Type; import com.android.jack.dx.rop.type.TypeList; @@ -27,96 +26,96 @@ import com.android.jack.dx.util.Hex; * Representation of a list of class references. */ public final class TypeListItem extends OffsettedItem { - /** alignment requirement */ - private static final int ALIGNMENT = 4; - - /** element size in bytes */ - private static final int ELEMENT_SIZE = 2; - - /** header size in bytes */ - private static final int HEADER_SIZE = 4; - - /** {@code non-null;} the actual list */ - private final TypeList list; - - /** - * Constructs an instance. - * - * @param list {@code non-null;} the actual list - */ - public TypeListItem(TypeList list) { - super(ALIGNMENT, (list.size() * ELEMENT_SIZE) + HEADER_SIZE); - - this.list = list; - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return StdTypeList.hashContents(list); + /** alignment requirement */ + private static final int ALIGNMENT = 4; + + /** element size in bytes */ + private static final int ELEMENT_SIZE = 2; + + /** header size in bytes */ + private static final int HEADER_SIZE = 4; + + /** {@code non-null;} the actual list */ + private final TypeList list; + + /** + * Constructs an instance. + * + * @param list {@code non-null;} the actual list + */ + public TypeListItem(TypeList list) { + super(ALIGNMENT, (list.size() * ELEMENT_SIZE) + HEADER_SIZE); + + this.list = list; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return StdTypeList.hashContents(list); + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return ItemType.TYPE_TYPE_LIST; + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + TypeIdsSection typeIds = file.getTypeIds(); + int sz = list.size(); + + for (int i = 0; i < sz; i++) { + typeIds.intern(list.getType(i)); } - - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return ItemType.TYPE_TYPE_LIST; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + throw new RuntimeException("unsupported"); + } + + /** + * Gets the underlying list. + * + * @return {@code non-null;} the list + */ + public TypeList getList() { + return list; + } + + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + TypeIdsSection typeIds = file.getTypeIds(); + int sz = list.size(); + + if (out.annotates()) { + out.annotate(0, offsetString() + " type_list"); + out.annotate(HEADER_SIZE, " size: " + Hex.u4(sz)); + for (int i = 0; i < sz; i++) { + Type one = list.getType(i); + int idx = typeIds.indexOf(one); + out.annotate(ELEMENT_SIZE, " " + Hex.u2(idx) + " // " + one.toHuman()); + } } - /** {@inheritDoc} */ - public void addContents(DexFile file) { - TypeIdsSection typeIds = file.getTypeIds(); - int sz = list.size(); + out.writeInt(sz); - for (int i = 0; i < sz; i++) { - typeIds.intern(list.getType(i)); - } + for (int i = 0; i < sz; i++) { + out.writeShort(typeIds.indexOf(list.getType(i))); } + } - /** {@inheritDoc} */ - @Override - public String toHuman() { - throw new RuntimeException("unsupported"); - } - - /** - * Gets the underlying list. - * - * @return {@code non-null;} the list - */ - public TypeList getList() { - return list; - } + /** {@inheritDoc} */ + @Override + protected int compareTo0(OffsettedItem other) { + TypeList thisList = this.list; + TypeList otherList = ((TypeListItem) other).list; - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - TypeIdsSection typeIds = file.getTypeIds(); - int sz = list.size(); - - if (out.annotates()) { - out.annotate(0, offsetString() + " type_list"); - out.annotate(HEADER_SIZE, " size: " + Hex.u4(sz)); - for (int i = 0; i < sz; i++) { - Type one = list.getType(i); - int idx = typeIds.indexOf(one); - out.annotate(ELEMENT_SIZE, - " " + Hex.u2(idx) + " // " + one.toHuman()); - } - } - - out.writeInt(sz); - - for (int i = 0; i < sz; i++) { - out.writeShort(typeIds.indexOf(list.getType(i))); - } - } - - /** {@inheritDoc} */ - @Override - protected int compareTo0(OffsettedItem other) { - TypeList thisList = this.list; - TypeList otherList = ((TypeListItem) other).list; - - return StdTypeList.compareContents(thisList, otherList); - } + return StdTypeList.compareContents(thisList, otherList); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/UniformItemSection.java b/dx/src/com/android/jack/dx/dex/file/UniformItemSection.java index 0e4502d..82fea3b 100644 --- a/dx/src/com/android/jack/dx/dex/file/UniformItemSection.java +++ b/dx/src/com/android/jack/dx/dex/file/UniformItemSection.java @@ -27,86 +27,86 @@ import java.util.Collection; * the output. */ public abstract class UniformItemSection extends Section { - /** - * Constructs an instance. The file offset is initially unknown. - * - * @param name {@code null-ok;} the name of this instance, for annotation - * purposes - * @param file {@code non-null;} file that this instance is part of - * @param alignment {@code > 0;} alignment requirement for the final output; - * must be a power of 2 - */ - public UniformItemSection(String name, DexFile file, int alignment) { - super(name, file, alignment); - } - - /** {@inheritDoc} */ - @Override - public final int writeSize() { - Collection<? extends Item> items = items(); - int sz = items.size(); - - if (sz == 0) { - return 0; - } - - // Since each item has to be the same size, we can pick any. - return sz * items.iterator().next().writeSize(); + /** + * Constructs an instance. The file offset is initially unknown. + * + * @param name {@code null-ok;} the name of this instance, for annotation + * purposes + * @param file {@code non-null;} file that this instance is part of + * @param alignment {@code > 0;} alignment requirement for the final output; + * must be a power of 2 + */ + public UniformItemSection(String name, DexFile file, int alignment) { + super(name, file, alignment); + } + + /** {@inheritDoc} */ + @Override + public final int writeSize() { + Collection<? extends Item> items = items(); + int sz = items.size(); + + if (sz == 0) { + return 0; } - /** - * Gets the item corresponding to the given {@link Constant}. This - * will throw an exception if the constant is not found, including - * if this instance isn't the sort that maps constants to {@link - * IndexedItem} instances. - * - * @param cst {@code non-null;} constant to look for - * @return {@code non-null;} the corresponding item found in this instance - */ - public abstract IndexedItem get(Constant cst); - - /** {@inheritDoc} */ - @Override - protected final void prepare0() { - DexFile file = getFile(); - - orderItems(); - - for (Item one : items()) { - one.addContents(file); - } + // Since each item has to be the same size, we can pick any. + return sz * items.iterator().next().writeSize(); + } + + /** + * Gets the item corresponding to the given {@link Constant}. This + * will throw an exception if the constant is not found, including + * if this instance isn't the sort that maps constants to {@link + * IndexedItem} instances. + * + * @param cst {@code non-null;} constant to look for + * @return {@code non-null;} the corresponding item found in this instance + */ + public abstract IndexedItem get(Constant cst); + + /** {@inheritDoc} */ + @Override + protected final void prepare0() { + DexFile file = getFile(); + + orderItems(); + + for (Item one : items()) { + one.addContents(file); } + } - /** {@inheritDoc} */ - @Override - protected final void writeTo0(AnnotatedOutput out) { - DexFile file = getFile(); - int alignment = getAlignment(); + /** {@inheritDoc} */ + @Override + protected final void writeTo0(AnnotatedOutput out) { + DexFile file = getFile(); + int alignment = getAlignment(); - for (Item one : items()) { - one.writeTo(file, out); - out.alignTo(alignment); - } + for (Item one : items()) { + one.writeTo(file, out); + out.alignTo(alignment); } - - /** {@inheritDoc} */ - @Override - public final int getAbsoluteItemOffset(Item item) { - /* - * Since all items must be the same size, we can use the size - * of the one we're given to calculate its offset. - */ - IndexedItem ii = (IndexedItem) item; - int relativeOffset = ii.getIndex() * ii.writeSize(); - - return getAbsoluteOffset(relativeOffset); - } - - /** - * Alters or picks the order for items in this instance if desired, - * so that subsequent calls to {@link #items} will yield a - * so-ordered collection. If the items in this instance are indexed, - * then this method should also assign indices. + } + + /** {@inheritDoc} */ + @Override + public final int getAbsoluteItemOffset(Item item) { + /* + * Since all items must be the same size, we can use the size + * of the one we're given to calculate its offset. */ - protected abstract void orderItems(); + IndexedItem ii = (IndexedItem) item; + int relativeOffset = ii.getIndex() * ii.writeSize(); + + return getAbsoluteOffset(relativeOffset); + } + + /** + * Alters or picks the order for items in this instance if desired, + * so that subsequent calls to {@link #items} will yield a + * so-ordered collection. If the items in this instance are indexed, + * then this method should also assign indices. + */ + protected abstract void orderItems(); } diff --git a/dx/src/com/android/jack/dx/dex/file/UniformListItem.java b/dx/src/com/android/jack/dx/dex/file/UniformListItem.java index 1046c19..9eb36ae 100644 --- a/dx/src/com/android/jack/dx/dex/file/UniformListItem.java +++ b/dx/src/com/android/jack/dx/dex/file/UniformListItem.java @@ -19,7 +19,6 @@ package com.android.jack.dx.dex.file; import com.android.jack.dx.util.AnnotatedOutput; import com.android.jack.dx.util.Hex; -import java.util.HashMap; import java.util.List; /** @@ -34,183 +33,180 @@ import java.util.List; * * @param <T> type of element contained in an instance */ -public final class UniformListItem<T extends OffsettedItem> - extends OffsettedItem { - /** the size of the list header */ - private static final int HEADER_SIZE = 4; - - /** {@code non-null;} the item type */ - private final ItemType itemType; - - /** {@code non-null;} the contents */ - private final List<T> items; - - /** - * Constructs an instance. It is illegal to modify the given list once - * it is used to construct an instance of this class. - * - * @param itemType {@code non-null;} the type of the item - * @param items {@code non-null and non-empty;} list of items to represent - */ - public UniformListItem(ItemType itemType, List<T> items) { - super(getAlignment(items), writeSize(items)); - - if (itemType == null) { - throw new NullPointerException("itemType == null"); - } - - this.items = items; - this.itemType = itemType; +public final class UniformListItem<T extends OffsettedItem> extends OffsettedItem { + /** the size of the list header */ + private static final int HEADER_SIZE = 4; + + /** {@code non-null;} the item type */ + private final ItemType itemType; + + /** {@code non-null;} the contents */ + private final List<T> items; + + /** + * Constructs an instance. It is illegal to modify the given list once + * it is used to construct an instance of this class. + * + * @param itemType {@code non-null;} the type of the item + * @param items {@code non-null and non-empty;} list of items to represent + */ + public UniformListItem(ItemType itemType, List<T> items) { + super(getAlignment(items), writeSize(items)); + + if (itemType == null) { + throw new NullPointerException("itemType == null"); } - /** - * Helper for {@link #UniformListItem}, which returns the alignment - * requirement implied by the given list. See the header comment for - * more details. - * - * @param items {@code non-null;} list of items being represented - * @return {@code >= 4;} the alignment requirement - */ - private static int getAlignment(List<? extends OffsettedItem> items) { - try { - // Since they all must have the same alignment, any one will do. - return Math.max(HEADER_SIZE, items.get(0).getAlignment()); - } catch (IndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("items.size() == 0"); - } catch (NullPointerException ex) { - // Translate the exception. - throw new NullPointerException("items == null"); - } + this.items = items; + this.itemType = itemType; + } + + /** + * Helper for {@link #UniformListItem}, which returns the alignment + * requirement implied by the given list. See the header comment for + * more details. + * + * @param items {@code non-null;} list of items being represented + * @return {@code >= 4;} the alignment requirement + */ + private static int getAlignment(List<? extends OffsettedItem> items) { + try { + // Since they all must have the same alignment, any one will do. + return Math.max(HEADER_SIZE, items.get(0).getAlignment()); + } catch (IndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("items.size() == 0"); + } catch (NullPointerException ex) { + // Translate the exception. + throw new NullPointerException("items == null"); } - - /** - * Calculates the write size for the given list. - * - * @param items {@code non-null;} the list in question - * @return {@code >= 0;} the write size + } + + /** + * Calculates the write size for the given list. + * + * @param items {@code non-null;} the list in question + * @return {@code >= 0;} the write size + */ + private static int writeSize(List<? extends OffsettedItem> items) { + /* + * This class assumes all included items are the same size, + * an assumption which is verified in place0(). */ - private static int writeSize(List<? extends OffsettedItem> items) { - /* - * This class assumes all included items are the same size, - * an assumption which is verified in place0(). - */ - OffsettedItem first = items.get(0); - return (items.size() * first.writeSize()) + getAlignment(items); - } - - /** {@inheritDoc} */ - @Override - public ItemType itemType() { - return itemType; + OffsettedItem first = items.get(0); + return (items.size() * first.writeSize()) + getAlignment(items); + } + + /** {@inheritDoc} */ + @Override + public ItemType itemType() { + return itemType; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(100); + + sb.append(getClass().getName()); + sb.append(items); + + return sb.toString(); + } + + /** {@inheritDoc} */ + @Override + public void addContents(DexFile file) { + for (OffsettedItem i : items) { + i.addContents(file); } - - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(100); - - sb.append(getClass().getName()); - sb.append(items); - - return sb.toString(); + } + + /** {@inheritDoc} */ + @Override + public final String toHuman() { + StringBuffer sb = new StringBuffer(100); + boolean first = true; + + sb.append("{"); + + for (OffsettedItem i : items) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append(i.toHuman()); } - /** {@inheritDoc} */ - @Override - public void addContents(DexFile file) { - for (OffsettedItem i : items) { - i.addContents(file); + sb.append("}"); + return sb.toString(); + } + + /** + * Gets the underlying list of items. + * + * @return {@code non-null;} the list + */ + public final List<T> getItems() { + return items; + } + + /** {@inheritDoc} */ + @Override + protected void place0(Section addedTo, int offset) { + offset += headerSize(); + + boolean first = true; + int theSize = -1; + int theAlignment = -1; + + for (OffsettedItem i : items) { + int size = i.writeSize(); + if (first) { + theSize = size; + theAlignment = i.getAlignment(); + first = false; + } else { + if (size != theSize) { + throw new UnsupportedOperationException("item size mismatch"); } - } - - /** {@inheritDoc} */ - @Override - public final String toHuman() { - StringBuffer sb = new StringBuffer(100); - boolean first = true; - - sb.append("{"); - - for (OffsettedItem i : items) { - if (first) { - first = false; - } else { - sb.append(", "); - } - sb.append(i.toHuman()); + if (i.getAlignment() != theAlignment) { + throw new UnsupportedOperationException("item alignment mismatch"); } + } - sb.append("}"); - return sb.toString(); + offset = i.place(addedTo, offset) + size; } + } - /** - * Gets the underlying list of items. - * - * @return {@code non-null;} the list - */ - public final List<T> getItems() { - return items; - } + /** {@inheritDoc} */ + @Override + protected void writeTo0(DexFile file, AnnotatedOutput out) { + int size = items.size(); - /** {@inheritDoc} */ - @Override - protected void place0(Section addedTo, int offset) { - offset += headerSize(); - - boolean first = true; - int theSize = -1; - int theAlignment = -1; - - for (OffsettedItem i : items) { - int size = i.writeSize(); - if (first) { - theSize = size; - theAlignment = i.getAlignment(); - first = false; - } else { - if (size != theSize) { - throw new UnsupportedOperationException( - "item size mismatch"); - } - if (i.getAlignment() != theAlignment) { - throw new UnsupportedOperationException( - "item alignment mismatch"); - } - } - - offset = i.place(addedTo, offset) + size; - } + if (out.annotates()) { + out.annotate(0, offsetString() + " " + typeName()); + out.annotate(4, " size: " + Hex.u4(size)); } - /** {@inheritDoc} */ - @Override - protected void writeTo0(DexFile file, AnnotatedOutput out) { - int size = items.size(); + out.writeInt(size); - if (out.annotates()) { - out.annotate(0, offsetString() + " " + typeName()); - out.annotate(4, " size: " + Hex.u4(size)); - } - - out.writeInt(size); - - for (OffsettedItem i : items) { - i.writeTo(file, out); - } + for (OffsettedItem i : items) { + i.writeTo(file, out); } - - /** - * Get the size of the header of this list. - * - * @return {@code >= 0;} the header size + } + + /** + * Get the size of the header of this list. + * + * @return {@code >= 0;} the header size + */ + private int headerSize() { + /* + * Because of how this instance was set up, this is the same + * as the alignment. */ - private int headerSize() { - /* - * Because of how this instance was set up, this is the same - * as the alignment. - */ - return getAlignment(); - } + return getAlignment(); + } } diff --git a/dx/src/com/android/jack/dx/dex/file/ValueEncoder.java b/dx/src/com/android/jack/dx/dex/file/ValueEncoder.java index c8a8019..d671ac9 100644 --- a/dx/src/com/android/jack/dx/dex/file/ValueEncoder.java +++ b/dx/src/com/android/jack/dx/dex/file/ValueEncoder.java @@ -46,483 +46,477 @@ import java.util.Collection; * thereof. */ public final class ValueEncoder { - /** annotation value type constant: {@code byte} */ - private static final int VALUE_BYTE = 0x00; + /** annotation value type constant: {@code byte} */ + private static final int VALUE_BYTE = 0x00; - /** annotation value type constant: {@code short} */ - private static final int VALUE_SHORT = 0x02; + /** annotation value type constant: {@code short} */ + private static final int VALUE_SHORT = 0x02; - /** annotation value type constant: {@code char} */ - private static final int VALUE_CHAR = 0x03; + /** annotation value type constant: {@code char} */ + private static final int VALUE_CHAR = 0x03; - /** annotation value type constant: {@code int} */ - private static final int VALUE_INT = 0x04; + /** annotation value type constant: {@code int} */ + private static final int VALUE_INT = 0x04; - /** annotation value type constant: {@code long} */ - private static final int VALUE_LONG = 0x06; + /** annotation value type constant: {@code long} */ + private static final int VALUE_LONG = 0x06; - /** annotation value type constant: {@code float} */ - private static final int VALUE_FLOAT = 0x10; + /** annotation value type constant: {@code float} */ + private static final int VALUE_FLOAT = 0x10; - /** annotation value type constant: {@code double} */ - private static final int VALUE_DOUBLE = 0x11; + /** annotation value type constant: {@code double} */ + private static final int VALUE_DOUBLE = 0x11; - /** annotation value type constant: {@code string} */ - private static final int VALUE_STRING = 0x17; + /** annotation value type constant: {@code string} */ + private static final int VALUE_STRING = 0x17; - /** annotation value type constant: {@code type} */ - private static final int VALUE_TYPE = 0x18; + /** annotation value type constant: {@code type} */ + private static final int VALUE_TYPE = 0x18; - /** annotation value type constant: {@code field} */ - private static final int VALUE_FIELD = 0x19; + /** annotation value type constant: {@code field} */ + private static final int VALUE_FIELD = 0x19; - /** annotation value type constant: {@code method} */ - private static final int VALUE_METHOD = 0x1a; + /** annotation value type constant: {@code method} */ + private static final int VALUE_METHOD = 0x1a; - /** annotation value type constant: {@code enum} */ - private static final int VALUE_ENUM = 0x1b; + /** annotation value type constant: {@code enum} */ + private static final int VALUE_ENUM = 0x1b; - /** annotation value type constant: {@code array} */ - private static final int VALUE_ARRAY = 0x1c; + /** annotation value type constant: {@code array} */ + private static final int VALUE_ARRAY = 0x1c; - /** annotation value type constant: {@code annotation} */ - private static final int VALUE_ANNOTATION = 0x1d; + /** annotation value type constant: {@code annotation} */ + private static final int VALUE_ANNOTATION = 0x1d; - /** annotation value type constant: {@code null} */ - private static final int VALUE_NULL = 0x1e; + /** annotation value type constant: {@code null} */ + private static final int VALUE_NULL = 0x1e; - /** annotation value type constant: {@code boolean} */ - private static final int VALUE_BOOLEAN = 0x1f; + /** annotation value type constant: {@code boolean} */ + private static final int VALUE_BOOLEAN = 0x1f; - /** {@code non-null;} file being written */ - private final DexFile file; + /** {@code non-null;} file being written */ + private final DexFile file; - /** {@code non-null;} output stream to write to */ - private final AnnotatedOutput out; + /** {@code non-null;} output stream to write to */ + private final AnnotatedOutput out; - /** - * Construct an instance. - * - * @param file {@code non-null;} file being written - * @param out {@code non-null;} output stream to write to - */ - public ValueEncoder(DexFile file, AnnotatedOutput out) { - if (file == null) { - throw new NullPointerException("file == null"); - } - - if (out == null) { - throw new NullPointerException("out == null"); - } - - this.file = file; - this.out = out; + /** + * Construct an instance. + * + * @param file {@code non-null;} file being written + * @param out {@code non-null;} output stream to write to + */ + public ValueEncoder(DexFile file, AnnotatedOutput out) { + if (file == null) { + throw new NullPointerException("file == null"); } - /** - * Writes out the encoded form of the given constant. - * - * @param cst {@code non-null;} the constant to write - */ - public void writeConstant(Constant cst) { - int type = constantToValueType(cst); - int arg; - - switch (type) { - case VALUE_BYTE: - case VALUE_SHORT: - case VALUE_INT: - case VALUE_LONG: { - long value = ((CstLiteralBits) cst).getLongBits(); - writeSignedIntegralValue(type, value); - break; - } - case VALUE_CHAR: { - long value = ((CstLiteralBits) cst).getLongBits(); - writeUnsignedIntegralValue(type, value); - break; - } - case VALUE_FLOAT: { - // Shift value left 32 so that right-zero-extension works. - long value = ((CstFloat) cst).getLongBits() << 32; - writeRightZeroExtendedValue(type, value); - break; - } - case VALUE_DOUBLE: { - long value = ((CstDouble) cst).getLongBits(); - writeRightZeroExtendedValue(type, value); - break; - } - case VALUE_STRING: { - int index = file.getStringIds().indexOf((CstString) cst); - writeUnsignedIntegralValue(type, (long) index); - break; - } - case VALUE_TYPE: { - int index = file.getTypeIds().indexOf((CstType) cst); - writeUnsignedIntegralValue(type, (long) index); - break; - } - case VALUE_FIELD: { - int index = file.getFieldIds().indexOf((CstFieldRef) cst); - writeUnsignedIntegralValue(type, (long) index); - break; - } - case VALUE_METHOD: { - int index = file.getMethodIds().indexOf((CstMethodRef) cst); - writeUnsignedIntegralValue(type, (long) index); - break; - } - case VALUE_ENUM: { - CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef(); - int index = file.getFieldIds().indexOf(fieldRef); - writeUnsignedIntegralValue(type, (long) index); - break; - } - case VALUE_ARRAY: { - out.writeByte(type); - writeArray((CstArray) cst, false); - break; - } - case VALUE_ANNOTATION: { - out.writeByte(type); - writeAnnotation(((CstAnnotation) cst).getAnnotation(), - false); - break; - } - case VALUE_NULL: { - out.writeByte(type); - break; - } - case VALUE_BOOLEAN: { - int value = ((CstBoolean) cst).getIntBits(); - out.writeByte(type | (value << 5)); - break; - } - default: { - throw new RuntimeException("Shouldn't happen"); - } - } + if (out == null) { + throw new NullPointerException("out == null"); } - /** - * Gets the value type for the given constant. - * - * @param cst {@code non-null;} the constant - * @return the value type; one of the {@code VALUE_*} constants - * defined by this class - */ - private static int constantToValueType(Constant cst) { - /* - * TODO: Constant should probable have an associated enum, so this - * can be a switch(). - */ - if (cst instanceof CstByte) { - return VALUE_BYTE; - } else if (cst instanceof CstShort) { - return VALUE_SHORT; - } else if (cst instanceof CstChar) { - return VALUE_CHAR; - } else if (cst instanceof CstInteger) { - return VALUE_INT; - } else if (cst instanceof CstLong) { - return VALUE_LONG; - } else if (cst instanceof CstFloat) { - return VALUE_FLOAT; - } else if (cst instanceof CstDouble) { - return VALUE_DOUBLE; - } else if (cst instanceof CstString) { - return VALUE_STRING; - } else if (cst instanceof CstType) { - return VALUE_TYPE; - } else if (cst instanceof CstFieldRef) { - return VALUE_FIELD; - } else if (cst instanceof CstMethodRef) { - return VALUE_METHOD; - } else if (cst instanceof CstEnumRef) { - return VALUE_ENUM; - } else if (cst instanceof CstArray) { - return VALUE_ARRAY; - } else if (cst instanceof CstAnnotation) { - return VALUE_ANNOTATION; - } else if (cst instanceof CstKnownNull) { - return VALUE_NULL; - } else if (cst instanceof CstBoolean) { - return VALUE_BOOLEAN; - } else { - throw new RuntimeException("Shouldn't happen"); - } + this.file = file; + this.out = out; + } + + /** + * Writes out the encoded form of the given constant. + * + * @param cst {@code non-null;} the constant to write + */ + public void writeConstant(Constant cst) { + int type = constantToValueType(cst); + + switch (type) { + case VALUE_BYTE: + case VALUE_SHORT: + case VALUE_INT: + case VALUE_LONG: { + long value = ((CstLiteralBits) cst).getLongBits(); + writeSignedIntegralValue(type, value); + break; + } + case VALUE_CHAR: { + long value = ((CstLiteralBits) cst).getLongBits(); + writeUnsignedIntegralValue(type, value); + break; + } + case VALUE_FLOAT: { + // Shift value left 32 so that right-zero-extension works. + long value = ((CstFloat) cst).getLongBits() << 32; + writeRightZeroExtendedValue(type, value); + break; + } + case VALUE_DOUBLE: { + long value = ((CstDouble) cst).getLongBits(); + writeRightZeroExtendedValue(type, value); + break; + } + case VALUE_STRING: { + int index = file.getStringIds().indexOf((CstString) cst); + writeUnsignedIntegralValue(type, index); + break; + } + case VALUE_TYPE: { + int index = file.getTypeIds().indexOf((CstType) cst); + writeUnsignedIntegralValue(type, index); + break; + } + case VALUE_FIELD: { + int index = file.getFieldIds().indexOf((CstFieldRef) cst); + writeUnsignedIntegralValue(type, index); + break; + } + case VALUE_METHOD: { + int index = file.getMethodIds().indexOf((CstMethodRef) cst); + writeUnsignedIntegralValue(type, index); + break; + } + case VALUE_ENUM: { + CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef(); + int index = file.getFieldIds().indexOf(fieldRef); + writeUnsignedIntegralValue(type, index); + break; + } + case VALUE_ARRAY: { + out.writeByte(type); + writeArray((CstArray) cst, false); + break; + } + case VALUE_ANNOTATION: { + out.writeByte(type); + writeAnnotation(((CstAnnotation) cst).getAnnotation(), false); + break; + } + case VALUE_NULL: { + out.writeByte(type); + break; + } + case VALUE_BOOLEAN: { + int value = ((CstBoolean) cst).getIntBits(); + out.writeByte(type | (value << 5)); + break; + } + default: { + throw new RuntimeException("Shouldn't happen"); + } } - - /** - * Writes out the encoded form of the given array, that is, as - * an {@code encoded_array} and not including a - * {@code value_type} prefix. If the output stream keeps - * (debugging) annotations and {@code topLevel} is - * {@code true}, then this method will write (debugging) - * annotations. - * - * @param array {@code non-null;} array instance to write - * @param topLevel {@code true} iff the given annotation is the - * top-level annotation or {@code false} if it is a sub-annotation - * of some other annotation + } + + /** + * Gets the value type for the given constant. + * + * @param cst {@code non-null;} the constant + * @return the value type; one of the {@code VALUE_*} constants + * defined by this class + */ + private static int constantToValueType(Constant cst) { + /* + * TODO(dx team): Constant should probable have an associated enum, so this + * can be a switch(). */ - public void writeArray(CstArray array, boolean topLevel) { - boolean annotates = topLevel && out.annotates(); - CstArray.List list = ((CstArray) array).getList(); - int size = list.size(); - - if (annotates) { - out.annotate(" size: " + Hex.u4(size)); - } - - out.writeUleb128(size); - - for (int i = 0; i < size; i++) { - Constant cst = list.get(i); - if (annotates) { - out.annotate(" [" + Integer.toHexString(i) + "] " + - constantToHuman(cst)); - } - writeConstant(cst); - } - - if (annotates) { - out.endAnnotation(); - } + if (cst instanceof CstByte) { + return VALUE_BYTE; + } else if (cst instanceof CstShort) { + return VALUE_SHORT; + } else if (cst instanceof CstChar) { + return VALUE_CHAR; + } else if (cst instanceof CstInteger) { + return VALUE_INT; + } else if (cst instanceof CstLong) { + return VALUE_LONG; + } else if (cst instanceof CstFloat) { + return VALUE_FLOAT; + } else if (cst instanceof CstDouble) { + return VALUE_DOUBLE; + } else if (cst instanceof CstString) { + return VALUE_STRING; + } else if (cst instanceof CstType) { + return VALUE_TYPE; + } else if (cst instanceof CstFieldRef) { + return VALUE_FIELD; + } else if (cst instanceof CstMethodRef) { + return VALUE_METHOD; + } else if (cst instanceof CstEnumRef) { + return VALUE_ENUM; + } else if (cst instanceof CstArray) { + return VALUE_ARRAY; + } else if (cst instanceof CstAnnotation) { + return VALUE_ANNOTATION; + } else if (cst instanceof CstKnownNull) { + return VALUE_NULL; + } else if (cst instanceof CstBoolean) { + return VALUE_BOOLEAN; + } else { + throw new RuntimeException("Shouldn't happen"); + } + } + + /** + * Writes out the encoded form of the given array, that is, as + * an {@code encoded_array} and not including a + * {@code value_type} prefix. If the output stream keeps + * (debugging) annotations and {@code topLevel} is + * {@code true}, then this method will write (debugging) + * annotations. + * + * @param array {@code non-null;} array instance to write + * @param topLevel {@code true} iff the given annotation is the + * top-level annotation or {@code false} if it is a sub-annotation + * of some other annotation + */ + public void writeArray(CstArray array, boolean topLevel) { + boolean annotates = topLevel && out.annotates(); + CstArray.List list = array.getList(); + int size = list.size(); + + if (annotates) { + out.annotate(" size: " + Hex.u4(size)); } - /** - * Writes out the encoded form of the given annotation, that is, - * as an {@code encoded_annotation} and not including a - * {@code value_type} prefix. If the output stream keeps - * (debugging) annotations and {@code topLevel} is - * {@code true}, then this method will write (debugging) - * annotations. - * - * @param annotation {@code non-null;} annotation instance to write - * @param topLevel {@code true} iff the given annotation is the - * top-level annotation or {@code false} if it is a sub-annotation - * of some other annotation - */ - public void writeAnnotation(Annotation annotation, boolean topLevel) { - boolean annotates = topLevel && out.annotates(); - StringIdsSection stringIds = file.getStringIds(); - TypeIdsSection typeIds = file.getTypeIds(); + out.writeUleb128(size); - CstType type = annotation.getType(); - int typeIdx = typeIds.indexOf(type); + for (int i = 0; i < size; i++) { + Constant cst = list.get(i); + if (annotates) { + out.annotate(" [" + Integer.toHexString(i) + "] " + constantToHuman(cst)); + } + writeConstant(cst); + } - if (annotates) { - out.annotate(" type_idx: " + Hex.u4(typeIdx) + " // " + - type.toHuman()); - } + if (annotates) { + out.endAnnotation(); + } + } + + /** + * Writes out the encoded form of the given annotation, that is, + * as an {@code encoded_annotation} and not including a + * {@code value_type} prefix. If the output stream keeps + * (debugging) annotations and {@code topLevel} is + * {@code true}, then this method will write (debugging) + * annotations. + * + * @param annotation {@code non-null;} annotation instance to write + * @param topLevel {@code true} iff the given annotation is the + * top-level annotation or {@code false} if it is a sub-annotation + * of some other annotation + */ + public void writeAnnotation(Annotation annotation, boolean topLevel) { + boolean annotates = topLevel && out.annotates(); + StringIdsSection stringIds = file.getStringIds(); + TypeIdsSection typeIds = file.getTypeIds(); + + CstType type = annotation.getType(); + int typeIdx = typeIds.indexOf(type); + + if (annotates) { + out.annotate(" type_idx: " + Hex.u4(typeIdx) + " // " + type.toHuman()); + } - out.writeUleb128(typeIds.indexOf(annotation.getType())); + out.writeUleb128(typeIds.indexOf(annotation.getType())); - Collection<NameValuePair> pairs = annotation.getNameValuePairs(); - int size = pairs.size(); + Collection<NameValuePair> pairs = annotation.getNameValuePairs(); + int size = pairs.size(); - if (annotates) { - out.annotate(" size: " + Hex.u4(size)); - } + if (annotates) { + out.annotate(" size: " + Hex.u4(size)); + } - out.writeUleb128(size); + out.writeUleb128(size); - int at = 0; - for (NameValuePair pair : pairs) { - CstString name = pair.getName(); - int nameIdx = stringIds.indexOf(name); - Constant value = pair.getValue(); + int at = 0; + for (NameValuePair pair : pairs) { + CstString name = pair.getName(); + int nameIdx = stringIds.indexOf(name); + Constant value = pair.getValue(); - if (annotates) { - out.annotate(0, " elements[" + at + "]:"); - at++; - out.annotate(" name_idx: " + Hex.u4(nameIdx) + " // " + - name.toHuman()); - } + if (annotates) { + out.annotate(0, " elements[" + at + "]:"); + at++; + out.annotate(" name_idx: " + Hex.u4(nameIdx) + " // " + name.toHuman()); + } - out.writeUleb128(nameIdx); + out.writeUleb128(nameIdx); - if (annotates) { - out.annotate(" value: " + constantToHuman(value)); - } + if (annotates) { + out.annotate(" value: " + constantToHuman(value)); + } - writeConstant(value); - } + writeConstant(value); + } - if (annotates) { - out.endAnnotation(); - } + if (annotates) { + out.endAnnotation(); + } + } + + /** + * Gets the colloquial type name and human form of the type of the + * given constant, when used as an encoded value. + * + * @param cst {@code non-null;} the constant + * @return {@code non-null;} its type name and human form + */ + public static String constantToHuman(Constant cst) { + int type = constantToValueType(cst); + + if (type == VALUE_NULL) { + return "null"; } - /** - * Gets the colloquial type name and human form of the type of the - * given constant, when used as an encoded value. - * - * @param cst {@code non-null;} the constant - * @return {@code non-null;} its type name and human form + StringBuilder sb = new StringBuilder(); + + sb.append(cst.typeName()); + sb.append(' '); + sb.append(cst.toHuman()); + + return sb.toString(); + } + + /** + * Helper for {@link #writeConstant}, which writes out the value + * for any signed integral type. + * + * @param type the type constant + * @param value {@code long} bits of the value + */ + private void writeSignedIntegralValue(int type, long value) { + /* + * Figure out how many bits are needed to represent the value, + * including a sign bit: The bit count is subtracted from 65 + * and not 64 to account for the sign bit. The xor operation + * has the effect of leaving non-negative values alone and + * unary complementing negative values (so that a leading zero + * count always returns a useful number for our present + * purpose). */ - public static String constantToHuman(Constant cst) { - int type = constantToValueType(cst); + int requiredBits = 65 - Long.numberOfLeadingZeros(value ^ (value >> 63)); - if (type == VALUE_NULL) { - return "null"; - } + // Round up the requiredBits to a number of bytes. + int requiredBytes = (requiredBits + 0x07) >> 3; - StringBuilder sb = new StringBuilder(); - - sb.append(cst.typeName()); - sb.append(' '); - sb.append(cst.toHuman()); + /* + * Write the header byte, which includes the type and + * requiredBytes - 1. + */ + out.writeByte(type | ((requiredBytes - 1) << 5)); - return sb.toString(); + // Write the value, per se. + while (requiredBytes > 0) { + out.writeByte((byte) value); + value >>= 8; + requiredBytes--; } - - /** - * Helper for {@link #writeConstant}, which writes out the value - * for any signed integral type. - * - * @param type the type constant - * @param value {@code long} bits of the value - */ - private void writeSignedIntegralValue(int type, long value) { - /* - * Figure out how many bits are needed to represent the value, - * including a sign bit: The bit count is subtracted from 65 - * and not 64 to account for the sign bit. The xor operation - * has the effect of leaving non-negative values alone and - * unary complementing negative values (so that a leading zero - * count always returns a useful number for our present - * purpose). - */ - int requiredBits = - 65 - Long.numberOfLeadingZeros(value ^ (value >> 63)); - - // Round up the requiredBits to a number of bytes. - int requiredBytes = (requiredBits + 0x07) >> 3; - - /* - * Write the header byte, which includes the type and - * requiredBytes - 1. - */ - out.writeByte(type | ((requiredBytes - 1) << 5)); - - // Write the value, per se. - while (requiredBytes > 0) { - out.writeByte((byte) value); - value >>= 8; - requiredBytes--; - } + } + + /** + * Helper for {@link #writeConstant}, which writes out the value + * for any unsigned integral type. + * + * @param type the type constant + * @param value {@code long} bits of the value + */ + private void writeUnsignedIntegralValue(int type, long value) { + // Figure out how many bits are needed to represent the value. + int requiredBits = 64 - Long.numberOfLeadingZeros(value); + if (requiredBits == 0) { + requiredBits = 1; } - /** - * Helper for {@link #writeConstant}, which writes out the value - * for any unsigned integral type. - * - * @param type the type constant - * @param value {@code long} bits of the value - */ - private void writeUnsignedIntegralValue(int type, long value) { - // Figure out how many bits are needed to represent the value. - int requiredBits = 64 - Long.numberOfLeadingZeros(value); - if (requiredBits == 0) { - requiredBits = 1; - } - - // Round up the requiredBits to a number of bytes. - int requiredBytes = (requiredBits + 0x07) >> 3; - - /* - * Write the header byte, which includes the type and - * requiredBytes - 1. - */ - out.writeByte(type | ((requiredBytes - 1) << 5)); - - // Write the value, per se. - while (requiredBytes > 0) { - out.writeByte((byte) value); - value >>= 8; - requiredBytes--; - } - } + // Round up the requiredBits to a number of bytes. + int requiredBytes = (requiredBits + 0x07) >> 3; - /** - * Helper for {@link #writeConstant}, which writes out a - * right-zero-extended value. - * - * @param type the type constant - * @param value {@code long} bits of the value + /* + * Write the header byte, which includes the type and + * requiredBytes - 1. */ - private void writeRightZeroExtendedValue(int type, long value) { - // Figure out how many bits are needed to represent the value. - int requiredBits = 64 - Long.numberOfTrailingZeros(value); - if (requiredBits == 0) { - requiredBits = 1; - } - - // Round up the requiredBits to a number of bytes. - int requiredBytes = (requiredBits + 0x07) >> 3; - - // Scootch the first bits to be written down to the low-order bits. - value >>= 64 - (requiredBytes * 8); - - /* - * Write the header byte, which includes the type and - * requiredBytes - 1. - */ - out.writeByte(type | ((requiredBytes - 1) << 5)); - - // Write the value, per se. - while (requiredBytes > 0) { - out.writeByte((byte) value); - value >>= 8; - requiredBytes--; - } + out.writeByte(type | ((requiredBytes - 1) << 5)); + + // Write the value, per se. + while (requiredBytes > 0) { + out.writeByte((byte) value); + value >>= 8; + requiredBytes--; + } + } + + /** + * Helper for {@link #writeConstant}, which writes out a + * right-zero-extended value. + * + * @param type the type constant + * @param value {@code long} bits of the value + */ + private void writeRightZeroExtendedValue(int type, long value) { + // Figure out how many bits are needed to represent the value. + int requiredBits = 64 - Long.numberOfTrailingZeros(value); + if (requiredBits == 0) { + requiredBits = 1; } + // Round up the requiredBits to a number of bytes. + int requiredBytes = (requiredBits + 0x07) >> 3; - /** - * Helper for {@code addContents()} methods, which adds - * contents for a particular {@link Annotation}, calling itself - * recursively should it encounter a nested annotation. - * - * @param file {@code non-null;} the file to add to - * @param annotation {@code non-null;} the annotation to add contents for - */ - public static void addContents(DexFile file, Annotation annotation) { - TypeIdsSection typeIds = file.getTypeIds(); - StringIdsSection stringIds = file.getStringIds(); + // Scootch the first bits to be written down to the low-order bits. + value >>= 64 - (requiredBytes * 8); - typeIds.intern(annotation.getType()); + /* + * Write the header byte, which includes the type and + * requiredBytes - 1. + */ + out.writeByte(type | ((requiredBytes - 1) << 5)); - for (NameValuePair pair : annotation.getNameValuePairs()) { - stringIds.intern(pair.getName()); - addContents(file, pair.getValue()); - } + // Write the value, per se. + while (requiredBytes > 0) { + out.writeByte((byte) value); + value >>= 8; + requiredBytes--; } - - /** - * Helper for {@code addContents()} methods, which adds - * contents for a particular constant, calling itself recursively - * should it encounter a {@link CstArray} and calling {@link - * #addContents(DexFile,Annotation)} recursively should it - * encounter a {@link CstAnnotation}. - * - * @param file {@code non-null;} the file to add to - * @param cst {@code non-null;} the constant to add contents for - */ - public static void addContents(DexFile file, Constant cst) { - if (cst instanceof CstAnnotation) { - addContents(file, ((CstAnnotation) cst).getAnnotation()); - } else if (cst instanceof CstArray) { - CstArray.List list = ((CstArray) cst).getList(); - int size = list.size(); - for (int i = 0; i < size; i++) { - addContents(file, list.get(i)); - } - } else { - file.internIfAppropriate(cst); - } + } + + + /** + * Helper for {@code addContents()} methods, which adds + * contents for a particular {@link Annotation}, calling itself + * recursively should it encounter a nested annotation. + * + * @param file {@code non-null;} the file to add to + * @param annotation {@code non-null;} the annotation to add contents for + */ + public static void addContents(DexFile file, Annotation annotation) { + TypeIdsSection typeIds = file.getTypeIds(); + StringIdsSection stringIds = file.getStringIds(); + + typeIds.intern(annotation.getType()); + + for (NameValuePair pair : annotation.getNameValuePairs()) { + stringIds.intern(pair.getName()); + addContents(file, pair.getValue()); + } + } + + /** + * Helper for {@code addContents()} methods, which adds + * contents for a particular constant, calling itself recursively + * should it encounter a {@link CstArray} and calling {@link + * #addContents(DexFile,Annotation)} recursively should it + * encounter a {@link CstAnnotation}. + * + * @param file {@code non-null;} the file to add to + * @param cst {@code non-null;} the constant to add contents for + */ + public static void addContents(DexFile file, Constant cst) { + if (cst instanceof CstAnnotation) { + addContents(file, ((CstAnnotation) cst).getAnnotation()); + } else if (cst instanceof CstArray) { + CstArray.List list = ((CstArray) cst).getList(); + int size = list.size(); + for (int i = 0; i < size; i++) { + addContents(file, list.get(i)); + } + } else { + file.internIfAppropriate(cst); } + } } diff --git a/dx/src/com/android/jack/dx/io/Annotation.java b/dx/src/com/android/jack/dx/io/Annotation.java index 47987b1..5dfef2c 100644 --- a/dx/src/com/android/jack/dx/io/Annotation.java +++ b/dx/src/com/android/jack/dx/io/Annotation.java @@ -22,83 +22,85 @@ import com.android.jack.dx.util.Unsigned; * An annotation. */ public final class Annotation implements Comparable<Annotation> { - private final DexBuffer buffer; - private final byte visibility; - private final int typeIndex; - private final int[] names; - private final EncodedValue[] values; + private final DexBuffer buffer; + private final byte visibility; + private final int typeIndex; + private final int[] names; + private final EncodedValue[] values; - public Annotation(DexBuffer buffer, byte visibility, int typeIndex, int[] names, - EncodedValue[] values) { - this.buffer = buffer; - this.visibility = visibility; - this.typeIndex = typeIndex; - this.names = names; - this.values = values; - } + public Annotation(DexBuffer buffer, byte visibility, int typeIndex, int[] names, + EncodedValue[] values) { + this.buffer = buffer; + this.visibility = visibility; + this.typeIndex = typeIndex; + this.names = names; + this.values = values; + } - public byte getVisibility() { - return visibility; - } + public byte getVisibility() { + return visibility; + } - public int getTypeIndex() { - return typeIndex; - } + public int getTypeIndex() { + return typeIndex; + } - public int[] getNames() { - return names; - } + public int[] getNames() { + return names; + } - public EncodedValue[] getValues() { - return values; - } + public EncodedValue[] getValues() { + return values; + } - public void writeTo(DexBuffer.Section out) { - out.writeByte(visibility); - out.writeUleb128(typeIndex); - out.writeUleb128(names.length); - for (int i = 0; i < names.length; i++) { - out.writeUleb128(names[i]); - values[i].writeTo(out); - } + public void writeTo(DexBuffer.Section out) { + out.writeByte(visibility); + out.writeUleb128(typeIndex); + out.writeUleb128(names.length); + for (int i = 0; i < names.length; i++) { + out.writeUleb128(names[i]); + values[i].writeTo(out); } + } - @Override public int compareTo(Annotation other) { - if (typeIndex != other.typeIndex) { - return Unsigned.compare(typeIndex, other.typeIndex); - } - int size = Math.min(names.length, other.names.length); - for (int i = 0; i < size; i++) { - if (names[i] != other.names[i]) { - return Unsigned.compare(names[i], other.names[i]); - } - int compare = values[i].compareTo(other.values[i]); - if (compare != 0) { - return compare; - } - } - return names.length - other.names.length; + @Override + public int compareTo(Annotation other) { + if (typeIndex != other.typeIndex) { + return Unsigned.compare(typeIndex, other.typeIndex); + } + int size = Math.min(names.length, other.names.length); + for (int i = 0; i < size; i++) { + if (names[i] != other.names[i]) { + return Unsigned.compare(names[i], other.names[i]); + } + int compare = values[i].compareTo(other.values[i]); + if (compare != 0) { + return compare; + } } + return names.length - other.names.length; + } - @Override public String toString() { - if (buffer == null) { - return visibility + " " + typeIndex; - } + @Override + public String toString() { + if (buffer == null) { + return visibility + " " + typeIndex; + } - StringBuilder result = new StringBuilder(); - result.append(visibility); - result.append(" "); - result.append(buffer.typeNames().get(typeIndex)); - result.append("["); - for (int i = 0; i < names.length; i++) { - if (i > 0) { - result.append(", "); - } - result.append(buffer.strings().get(names[i])); - result.append("="); - result.append(values[i]); - } - result.append("]"); - return result.toString(); + StringBuilder result = new StringBuilder(); + result.append(visibility); + result.append(" "); + result.append(buffer.typeNames().get(typeIndex)); + result.append("["); + for (int i = 0; i < names.length; i++) { + if (i > 0) { + result.append(", "); + } + result.append(buffer.strings().get(names[i])); + result.append("="); + result.append(values[i]); } + result.append("]"); + return result.toString(); + } } diff --git a/dx/src/com/android/jack/dx/io/ClassData.java b/dx/src/com/android/jack/dx/io/ClassData.java index c51a651..ee9d133 100644 --- a/dx/src/com/android/jack/dx/io/ClassData.java +++ b/dx/src/com/android/jack/dx/io/ClassData.java @@ -16,89 +16,98 @@ package com.android.jack.dx.io; +/** + * TODO(jack team) + */ public final class ClassData { - private final Field[] staticFields; - private final Field[] instanceFields; - private final Method[] directMethods; - private final Method[] virtualMethods; - - public ClassData(Field[] staticFields, Field[] instanceFields, - Method[] directMethods, Method[] virtualMethods) { - this.staticFields = staticFields; - this.instanceFields = instanceFields; - this.directMethods = directMethods; - this.virtualMethods = virtualMethods; - } - - public Field[] getStaticFields() { - return staticFields; + private final Field[] staticFields; + private final Field[] instanceFields; + private final Method[] directMethods; + private final Method[] virtualMethods; + + public ClassData(Field[] staticFields, Field[] instanceFields, Method[] directMethods, + Method[] virtualMethods) { + this.staticFields = staticFields; + this.instanceFields = instanceFields; + this.directMethods = directMethods; + this.virtualMethods = virtualMethods; + } + + public Field[] getStaticFields() { + return staticFields; + } + + public Field[] getInstanceFields() { + return instanceFields; + } + + public Method[] getDirectMethods() { + return directMethods; + } + + public Method[] getVirtualMethods() { + return virtualMethods; + } + + public Field[] allFields() { + Field[] result = new Field[staticFields.length + instanceFields.length]; + System.arraycopy(staticFields, 0, result, 0, staticFields.length); + System.arraycopy(instanceFields, 0, result, staticFields.length, instanceFields.length); + return result; + } + + public Method[] allMethods() { + Method[] result = new Method[directMethods.length + virtualMethods.length]; + System.arraycopy(directMethods, 0, result, 0, directMethods.length); + System.arraycopy(virtualMethods, 0, result, directMethods.length, virtualMethods.length); + return result; + } + + /** + * TODO(jack team) + */ + public static class Field { + private final int fieldIndex; + private final int accessFlags; + + public Field(int fieldIndex, int accessFlags) { + this.fieldIndex = fieldIndex; + this.accessFlags = accessFlags; } - public Field[] getInstanceFields() { - return instanceFields; + public int getFieldIndex() { + return fieldIndex; } - public Method[] getDirectMethods() { - return directMethods; + public int getAccessFlags() { + return accessFlags; } - - public Method[] getVirtualMethods() { - return virtualMethods; + } + + /** + * TODO(jack team) + */ + public static class Method { + private final int methodIndex; + private final int accessFlags; + private final int codeOffset; + + public Method(int methodIndex, int accessFlags, int codeOffset) { + this.methodIndex = methodIndex; + this.accessFlags = accessFlags; + this.codeOffset = codeOffset; } - public Field[] allFields() { - Field[] result = new Field[staticFields.length + instanceFields.length]; - System.arraycopy(staticFields, 0, result, 0, staticFields.length); - System.arraycopy(instanceFields, 0, result, staticFields.length, instanceFields.length); - return result; + public int getMethodIndex() { + return methodIndex; } - public Method[] allMethods() { - Method[] result = new Method[directMethods.length + virtualMethods.length]; - System.arraycopy(directMethods, 0, result, 0, directMethods.length); - System.arraycopy(virtualMethods, 0, result, directMethods.length, virtualMethods.length); - return result; + public int getAccessFlags() { + return accessFlags; } - public static class Field { - private final int fieldIndex; - private final int accessFlags; - - public Field(int fieldIndex, int accessFlags) { - this.fieldIndex = fieldIndex; - this.accessFlags = accessFlags; - } - - public int getFieldIndex() { - return fieldIndex; - } - - public int getAccessFlags() { - return accessFlags; - } - } - - public static class Method { - private final int methodIndex; - private final int accessFlags; - private final int codeOffset; - - public Method(int methodIndex, int accessFlags, int codeOffset) { - this.methodIndex = methodIndex; - this.accessFlags = accessFlags; - this.codeOffset = codeOffset; - } - - public int getMethodIndex() { - return methodIndex; - } - - public int getAccessFlags() { - return accessFlags; - } - - public int getCodeOffset() { - return codeOffset; - } + public int getCodeOffset() { + return codeOffset; } + } } diff --git a/dx/src/com/android/jack/dx/io/ClassDef.java b/dx/src/com/android/jack/dx/io/ClassDef.java index 60f15e3..7128905 100644 --- a/dx/src/com/android/jack/dx/io/ClassDef.java +++ b/dx/src/com/android/jack/dx/io/ClassDef.java @@ -20,83 +20,91 @@ package com.android.jack.dx.io; * A type definition. */ public final class ClassDef { - public static final int NO_INDEX = -1; - private final DexBuffer buffer; - private final int offset; - private final int typeIndex; - private final int accessFlags; - private final int supertypeIndex; - private final int interfacesOffset; - private final int sourceFileIndex; - private final int annotationsOffset; - private final int classDataOffset; - private final int staticValuesOffset; + public static final int NO_INDEX = -1; + private final DexBuffer buffer; + private final int offset; + private final int typeIndex; + private final int accessFlags; + private final int supertypeIndex; + private final int interfacesOffset; + private final int sourceFileIndex; + private final int annotationsOffset; + private final int classDataOffset; + private final int staticValuesOffset; - public ClassDef(DexBuffer buffer, int offset, int typeIndex, int accessFlags, - int supertypeIndex, int interfacesOffset, int sourceFileIndex, - int annotationsOffset, int classDataOffset, int staticValuesOffset) { - this.buffer = buffer; - this.offset = offset; - this.typeIndex = typeIndex; - this.accessFlags = accessFlags; - this.supertypeIndex = supertypeIndex; - this.interfacesOffset = interfacesOffset; - this.sourceFileIndex = sourceFileIndex; - this.annotationsOffset = annotationsOffset; - this.classDataOffset = classDataOffset; - this.staticValuesOffset = staticValuesOffset; - } + public ClassDef(DexBuffer buffer, + int offset, + int typeIndex, + int accessFlags, + int supertypeIndex, + int interfacesOffset, + int sourceFileIndex, + int annotationsOffset, + int classDataOffset, + int staticValuesOffset) { + this.buffer = buffer; + this.offset = offset; + this.typeIndex = typeIndex; + this.accessFlags = accessFlags; + this.supertypeIndex = supertypeIndex; + this.interfacesOffset = interfacesOffset; + this.sourceFileIndex = sourceFileIndex; + this.annotationsOffset = annotationsOffset; + this.classDataOffset = classDataOffset; + this.staticValuesOffset = staticValuesOffset; + } - public int getOffset() { - return offset; - } + public int getOffset() { + return offset; + } - public int getTypeIndex() { - return typeIndex; - } + public int getTypeIndex() { + return typeIndex; + } - public int getSupertypeIndex() { - return supertypeIndex; - } + public int getSupertypeIndex() { + return supertypeIndex; + } - public int getInterfacesOffset() { - return interfacesOffset; - } + public int getInterfacesOffset() { + return interfacesOffset; + } - public short[] getInterfaces() { - return buffer.readTypeList(interfacesOffset).getTypes(); - } + public short[] getInterfaces() { + return buffer.readTypeList(interfacesOffset).getTypes(); + } - public int getAccessFlags() { - return accessFlags; - } + public int getAccessFlags() { + return accessFlags; + } - public int getSourceFileIndex() { - return sourceFileIndex; - } + public int getSourceFileIndex() { + return sourceFileIndex; + } - public int getAnnotationsOffset() { - return annotationsOffset; - } + public int getAnnotationsOffset() { + return annotationsOffset; + } - public int getClassDataOffset() { - return classDataOffset; - } + public int getClassDataOffset() { + return classDataOffset; + } - public int getStaticValuesOffset() { - return staticValuesOffset; - } + public int getStaticValuesOffset() { + return staticValuesOffset; + } - @Override public String toString() { - if (buffer == null) { - return typeIndex + " " + supertypeIndex; - } + @Override + public String toString() { + if (buffer == null) { + return typeIndex + " " + supertypeIndex; + } - StringBuilder result = new StringBuilder(); - result.append(buffer.typeNames().get(typeIndex)); - if (supertypeIndex != NO_INDEX) { - result.append(" extends ").append(buffer.typeNames().get(supertypeIndex)); - } - return result.toString(); + StringBuilder result = new StringBuilder(); + result.append(buffer.typeNames().get(typeIndex)); + if (supertypeIndex != NO_INDEX) { + result.append(" extends ").append(buffer.typeNames().get(supertypeIndex)); } + return result.toString(); + } } diff --git a/dx/src/com/android/jack/dx/io/Code.java b/dx/src/com/android/jack/dx/io/Code.java index 865369b..376d72e 100644 --- a/dx/src/com/android/jack/dx/io/Code.java +++ b/dx/src/com/android/jack/dx/io/Code.java @@ -16,109 +16,123 @@ package com.android.jack.dx.io; +/** + * TODO(jack team) + */ public final class Code { - private final int registersSize; - private final int insSize; - private final int outsSize; - private final int debugInfoOffset; - private final short[] instructions; - private final Try[] tries; - private final CatchHandler[] catchHandlers; - - public Code(int registersSize, int insSize, int outsSize, int debugInfoOffset, - short[] instructions, Try[] tries, CatchHandler[] catchHandlers) { - this.registersSize = registersSize; - this.insSize = insSize; - this.outsSize = outsSize; - this.debugInfoOffset = debugInfoOffset; - this.instructions = instructions; - this.tries = tries; - this.catchHandlers = catchHandlers; - } - - public int getRegistersSize() { - return registersSize; + private final int registersSize; + private final int insSize; + private final int outsSize; + private final int debugInfoOffset; + private final short[] instructions; + private final Try[] tries; + private final CatchHandler[] catchHandlers; + + public Code(int registersSize, + int insSize, + int outsSize, + int debugInfoOffset, + short[] instructions, + Try[] tries, + CatchHandler[] catchHandlers) { + this.registersSize = registersSize; + this.insSize = insSize; + this.outsSize = outsSize; + this.debugInfoOffset = debugInfoOffset; + this.instructions = instructions; + this.tries = tries; + this.catchHandlers = catchHandlers; + } + + public int getRegistersSize() { + return registersSize; + } + + public int getInsSize() { + return insSize; + } + + public int getOutsSize() { + return outsSize; + } + + public int getDebugInfoOffset() { + return debugInfoOffset; + } + + public short[] getInstructions() { + return instructions; + } + + public Try[] getTries() { + return tries; + } + + public CatchHandler[] getCatchHandlers() { + return catchHandlers; + } + + /** + * TODO(jack team) + */ + public static class Try { + final int startAddress; + final int instructionCount; + final int catchHandlerIndex; + + Try(int startAddress, int instructionCount, int catchHandlerIndex) { + this.startAddress = startAddress; + this.instructionCount = instructionCount; + this.catchHandlerIndex = catchHandlerIndex; } - public int getInsSize() { - return insSize; + public int getStartAddress() { + return startAddress; } - public int getOutsSize() { - return outsSize; + public int getInstructionCount() { + return instructionCount; } - public int getDebugInfoOffset() { - return debugInfoOffset; + /** + * Returns this try's catch handler <strong>index</strong>. Note that + * this is distinct from the its catch handler <strong>offset</strong>. + */ + public int getCatchHandlerIndex() { + return catchHandlerIndex; } - - public short[] getInstructions() { - return instructions; + } + + /** + * TODO(jack team) + */ + public static class CatchHandler { + final int[] typeIndexes; + final int[] addresses; + final int catchAllAddress; + final int offset; + + public CatchHandler(int[] typeIndexes, int[] addresses, int catchAllAddress, int offset) { + this.typeIndexes = typeIndexes; + this.addresses = addresses; + this.catchAllAddress = catchAllAddress; + this.offset = offset; } - public Try[] getTries() { - return tries; + public int[] getTypeIndexes() { + return typeIndexes; } - public CatchHandler[] getCatchHandlers() { - return catchHandlers; + public int[] getAddresses() { + return addresses; } - public static class Try { - final int startAddress; - final int instructionCount; - final int catchHandlerIndex; - - Try(int startAddress, int instructionCount, int catchHandlerIndex) { - this.startAddress = startAddress; - this.instructionCount = instructionCount; - this.catchHandlerIndex = catchHandlerIndex; - } - - public int getStartAddress() { - return startAddress; - } - - public int getInstructionCount() { - return instructionCount; - } - - /** - * Returns this try's catch handler <strong>index</strong>. Note that - * this is distinct from the its catch handler <strong>offset</strong>. - */ - public int getCatchHandlerIndex() { - return catchHandlerIndex; - } + public int getCatchAllAddress() { + return catchAllAddress; } - public static class CatchHandler { - final int[] typeIndexes; - final int[] addresses; - final int catchAllAddress; - final int offset; - - public CatchHandler(int[] typeIndexes, int[] addresses, int catchAllAddress, int offset) { - this.typeIndexes = typeIndexes; - this.addresses = addresses; - this.catchAllAddress = catchAllAddress; - this.offset = offset; - } - - public int[] getTypeIndexes() { - return typeIndexes; - } - - public int[] getAddresses() { - return addresses; - } - - public int getCatchAllAddress() { - return catchAllAddress; - } - - public int getOffset() { - return offset; - } + public int getOffset() { + return offset; } + } } diff --git a/dx/src/com/android/jack/dx/io/CodeReader.java b/dx/src/com/android/jack/dx/io/CodeReader.java index 0b791b1..b1c1d4d 100644 --- a/dx/src/com/android/jack/dx/io/CodeReader.java +++ b/dx/src/com/android/jack/dx/io/CodeReader.java @@ -23,99 +23,110 @@ import com.android.jack.dx.util.DexException; * Walks through a block of code and calls visitor call backs. */ public final class CodeReader { - private Visitor fallbackVisitor = null; - private Visitor stringVisitor = null; - private Visitor typeVisitor = null; - private Visitor fieldVisitor = null; - private Visitor methodVisitor = null; - - /** - * Sets {@code visitor} as the visitor for all instructions. - */ - public void setAllVisitors(Visitor visitor) { - fallbackVisitor = visitor; - stringVisitor = visitor; - typeVisitor = visitor; - fieldVisitor = visitor; - methodVisitor = visitor; + private Visitor fallbackVisitor = null; + private Visitor stringVisitor = null; + private Visitor typeVisitor = null; + private Visitor fieldVisitor = null; + private Visitor methodVisitor = null; + + /** + * Sets {@code visitor} as the visitor for all instructions. + */ + public void setAllVisitors(Visitor visitor) { + fallbackVisitor = visitor; + stringVisitor = visitor; + typeVisitor = visitor; + fieldVisitor = visitor; + methodVisitor = visitor; + } + + /** + * Sets {@code visitor} as the visitor for all instructions not + * otherwise handled. + */ + public void setFallbackVisitor(Visitor visitor) { + fallbackVisitor = visitor; + } + + /** + * Sets {@code visitor} as the visitor for all string instructions. + */ + public void setStringVisitor(Visitor visitor) { + stringVisitor = visitor; + } + + /** + * Sets {@code visitor} as the visitor for all type instructions. + */ + public void setTypeVisitor(Visitor visitor) { + typeVisitor = visitor; + } + + /** + * Sets {@code visitor} as the visitor for all field instructions. + */ + public void setFieldVisitor(Visitor visitor) { + fieldVisitor = visitor; + } + + /** + * Sets {@code visitor} as the visitor for all method instructions. + */ + public void setMethodVisitor(Visitor visitor) { + methodVisitor = visitor; + } + + public void visitAll(DecodedInstruction[] decodedInstructions) throws DexException { + int size = decodedInstructions.length; + + for (int i = 0; i < size; i++) { + DecodedInstruction one = decodedInstructions[i]; + if (one == null) { + continue; + } + + callVisit(decodedInstructions, one); } - - /** - * Sets {@code visitor} as the visitor for all instructions not - * otherwise handled. - */ - public void setFallbackVisitor(Visitor visitor) { - fallbackVisitor = visitor; - } - - /** - * Sets {@code visitor} as the visitor for all string instructions. - */ - public void setStringVisitor(Visitor visitor) { - stringVisitor = visitor; - } - - /** - * Sets {@code visitor} as the visitor for all type instructions. - */ - public void setTypeVisitor(Visitor visitor) { - typeVisitor = visitor; - } - - /** - * Sets {@code visitor} as the visitor for all field instructions. - */ - public void setFieldVisitor(Visitor visitor) { - fieldVisitor = visitor; - } - - /** - * Sets {@code visitor} as the visitor for all method instructions. - */ - public void setMethodVisitor(Visitor visitor) { - methodVisitor = visitor; + } + + public void visitAll(short[] encodedInstructions) throws DexException { + DecodedInstruction[] decodedInstructions = DecodedInstruction.decodeAll(encodedInstructions); + visitAll(decodedInstructions); + } + + private void callVisit(DecodedInstruction[] all, DecodedInstruction one) { + Visitor visitor = null; + + switch (OpcodeInfo.getIndexType(one.getOpcode())) { + case STRING_REF: + visitor = stringVisitor; + break; + case TYPE_REF: + visitor = typeVisitor; + break; + case FIELD_REF: + visitor = fieldVisitor; + break; + case METHOD_REF: + visitor = methodVisitor; + break; + default: + /* continue */ } - public void visitAll(DecodedInstruction[] decodedInstructions) - throws DexException { - int size = decodedInstructions.length; - - for (int i = 0; i < size; i++) { - DecodedInstruction one = decodedInstructions[i]; - if (one == null) { - continue; - } - - callVisit(decodedInstructions, one); - } - } - - public void visitAll(short[] encodedInstructions) throws DexException { - DecodedInstruction[] decodedInstructions = - DecodedInstruction.decodeAll(encodedInstructions); - visitAll(decodedInstructions); - } - - private void callVisit(DecodedInstruction[] all, DecodedInstruction one) { - Visitor visitor = null; - - switch (OpcodeInfo.getIndexType(one.getOpcode())) { - case STRING_REF: visitor = stringVisitor; break; - case TYPE_REF: visitor = typeVisitor; break; - case FIELD_REF: visitor = fieldVisitor; break; - case METHOD_REF: visitor = methodVisitor; break; - } - - if (visitor == null) { - visitor = fallbackVisitor; - } - - if (visitor != null) { - visitor.visit(all, one); - } + if (visitor == null) { + visitor = fallbackVisitor; } - public interface Visitor { - void visit(DecodedInstruction[] all, DecodedInstruction one); + if (visitor != null) { + visitor.visit(all, one); } + } + + /** + * TODO(jack team) + */ + public interface Visitor { + void visit(DecodedInstruction[] all, DecodedInstruction one); + } } diff --git a/dx/src/com/android/jack/dx/io/DexBuffer.java b/dx/src/com/android/jack/dx/io/DexBuffer.java index 2e8299c..e273f4b 100644 --- a/dx/src/com/android/jack/dx/io/DexBuffer.java +++ b/dx/src/com/android/jack/dx/io/DexBuffer.java @@ -51,660 +51,694 @@ import java.util.zip.ZipFile; * are unsigned. */ public final class DexBuffer { - private byte[] data; - private final TableOfContents tableOfContents = new TableOfContents(); - private int length = 0; - - private final List<String> strings = new AbstractList<String>() { - @Override public String get(int index) { - checkBounds(index, tableOfContents.stringIds.size); - return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)) - .readString(); - } - @Override public int size() { - return tableOfContents.stringIds.size; - } - }; - - private final List<Integer> typeIds = new AbstractList<Integer>() { - @Override public Integer get(int index) { - checkBounds(index, tableOfContents.typeIds.size); - return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt(); - } - @Override public int size() { - return tableOfContents.typeIds.size; - } - }; - - private final List<String> typeNames = new AbstractList<String>() { - @Override public String get(int index) { - checkBounds(index, tableOfContents.typeIds.size); - return strings.get(typeIds.get(index)); - } - @Override public int size() { - return tableOfContents.typeIds.size; - } - }; - - private final List<ProtoId> protoIds = new AbstractList<ProtoId>() { - @Override public ProtoId get(int index) { - checkBounds(index, tableOfContents.protoIds.size); - return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index)) - .readProtoId(); - } - @Override public int size() { - return tableOfContents.protoIds.size; - } - }; - - private final List<FieldId> fieldIds = new AbstractList<FieldId>() { - @Override public FieldId get(int index) { - checkBounds(index, tableOfContents.fieldIds.size); - return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index)) - .readFieldId(); - } - @Override public int size() { - return tableOfContents.fieldIds.size; - } - }; - - private final List<MethodId> methodIds = new AbstractList<MethodId>() { - @Override public MethodId get(int index) { - checkBounds(index, tableOfContents.methodIds.size); - return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index)) - .readMethodId(); - } - @Override public int size() { - return tableOfContents.methodIds.size; - } - }; - - /** - * Creates a new dex buffer defining no classes. - */ - public DexBuffer() { - this.data = new byte[0]; + private byte[] data; + private final TableOfContents tableOfContents = new TableOfContents(); + private int length = 0; + + private final List<String> strings = new AbstractList<String>() { + @Override + public String get(int index) { + checkBounds(index, tableOfContents.stringIds.size); + return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)).readString(); } - /** - * Creates a new dex buffer that reads from {@code data}. It is an error to - * modify {@code data} after using it to create a dex buffer. - */ - public DexBuffer(byte[] data) throws IOException { - this.data = data; - this.length = data.length; - this.tableOfContents.readFrom(this); + @Override + public int size() { + return tableOfContents.stringIds.size; } + }; - /** - * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}. - */ - public DexBuffer(InputStream in) throws IOException { - loadFrom(in); + private final List<Integer> typeIds = new AbstractList<Integer>() { + @Override + public Integer get(int index) { + checkBounds(index, tableOfContents.typeIds.size); + return open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt(); } - /** - * Creates a new dex buffer from the dex file {@code file}. - */ - public DexBuffer(File file) throws IOException { - if (FileUtils.hasArchiveSuffix(file.getName())) { - ZipFile zipFile = new ZipFile(file); - ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME); - if (entry != null) { - loadFrom(zipFile.getInputStream(entry)); - zipFile.close(); - } else { - throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file); - } - } else if (file.getName().endsWith(".dex")) { - loadFrom(new FileInputStream(file)); - } else { - throw new DexException("unknown output extension: " + file); - } + @Override + public int size() { + return tableOfContents.typeIds.size; } + }; - private void loadFrom(InputStream in) throws IOException { - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - byte[] buffer = new byte[8192]; - - int count; - while ((count = in.read(buffer)) != -1) { - bytesOut.write(buffer, 0, count); - } - in.close(); - - this.data = bytesOut.toByteArray(); - this.length = data.length; - this.tableOfContents.readFrom(this); + private final List<String> typeNames = new AbstractList<String>() { + @Override + public String get(int index) { + checkBounds(index, tableOfContents.typeIds.size); + return strings.get(typeIds.get(index)); } - private static void checkBounds(int index, int length) { - if (index < 0 || index >= length) { - throw new IndexOutOfBoundsException("index:" + index + ", length=" + length); - } + @Override + public int size() { + return tableOfContents.typeIds.size; } + }; - public void writeTo(OutputStream out) throws IOException { - out.write(data); + private final List<ProtoId> protoIds = new AbstractList<ProtoId>() { + @Override + public ProtoId get(int index) { + checkBounds(index, tableOfContents.protoIds.size); + return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index)).readProtoId(); } - public void writeTo(File dexOut) throws IOException { - OutputStream out = new FileOutputStream(dexOut); - writeTo(out); - out.close(); + @Override + public int size() { + return tableOfContents.protoIds.size; } + }; - public TableOfContents getTableOfContents() { - return tableOfContents; + private final List<FieldId> fieldIds = new AbstractList<FieldId>() { + @Override + public FieldId get(int index) { + checkBounds(index, tableOfContents.fieldIds.size); + return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index)).readFieldId(); } - public Section open(int position) { - if (position < 0 || position > length) { - throw new IllegalArgumentException("position=" + position + " length=" + length); - } - return new Section(position); + @Override + public int size() { + return tableOfContents.fieldIds.size; } + }; - public Section appendSection(int maxByteCount, String name) { - int limit = fourByteAlign(length + maxByteCount); - Section result = new Section(name, length, limit); - length = limit; - return result; + private final List<MethodId> methodIds = new AbstractList<MethodId>() { + @Override + public MethodId get(int index) { + checkBounds(index, tableOfContents.methodIds.size); + return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index)).readMethodId(); } - public void noMoreSections() { - data = new byte[length]; + @Override + public int size() { + return tableOfContents.methodIds.size; } - - public int getLength() { - return length; + }; + + /** + * Creates a new dex buffer defining no classes. + */ + public DexBuffer() { + this.data = new byte[0]; + } + + /** + * Creates a new dex buffer that reads from {@code data}. It is an error to + * modify {@code data} after using it to create a dex buffer. + */ + public DexBuffer(byte[] data) throws IOException { + this.data = data; + this.length = data.length; + this.tableOfContents.readFrom(this); + } + + /** + * Creates a new dex buffer of the dex in {@code in}, and closes {@code in}. + */ + public DexBuffer(InputStream in) throws IOException { + loadFrom(in); + } + + /** + * Creates a new dex buffer from the dex file {@code file}. + */ + public DexBuffer(File file) throws IOException { + if (FileUtils.hasArchiveSuffix(file.getName())) { + ZipFile zipFile = new ZipFile(file); + ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME); + if (entry != null) { + loadFrom(zipFile.getInputStream(entry)); + zipFile.close(); + } else { + throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file); + } + } else if (file.getName().endsWith(".dex")) { + loadFrom(new FileInputStream(file)); + } else { + throw new DexException("unknown output extension: " + file); } + } - public static int fourByteAlign(int position) { - return (position + 3) & ~3; - } + private void loadFrom(InputStream in) throws IOException { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; - public byte[] getBytes() { - return data; + int count; + while ((count = in.read(buffer)) != -1) { + bytesOut.write(buffer, 0, count); } + in.close(); - public List<String> strings() { - return strings; - } + this.data = bytesOut.toByteArray(); + this.length = data.length; + this.tableOfContents.readFrom(this); + } - public List<Integer> typeIds() { - return typeIds; + private static void checkBounds(int index, int length) { + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("index:" + index + ", length=" + length); } + } - public List<String> typeNames() { - return typeNames; - } + public void writeTo(OutputStream out) throws IOException { + out.write(data); + } - public List<ProtoId> protoIds() { - return protoIds; - } + public void writeTo(File dexOut) throws IOException { + OutputStream out = new FileOutputStream(dexOut); + writeTo(out); + out.close(); + } - public List<FieldId> fieldIds() { - return fieldIds; - } + public TableOfContents getTableOfContents() { + return tableOfContents; + } - public List<MethodId> methodIds() { - return methodIds; + public Section open(int position) { + if (position < 0 || position > length) { + throw new IllegalArgumentException("position=" + position + " length=" + length); } - - public Iterable<ClassDef> classDefs() { - return new Iterable<ClassDef>() { - public Iterator<ClassDef> iterator() { - if (!tableOfContents.classDefs.exists()) { - return Collections.<ClassDef>emptySet().iterator(); - } - return new Iterator<ClassDef>() { - private DexBuffer.Section in = open(tableOfContents.classDefs.off); - private int count = 0; - - public boolean hasNext() { - return count < tableOfContents.classDefs.size; - } - public ClassDef next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - count++; - return in.readClassDef(); - } - public void remove() { - throw new UnsupportedOperationException(); - } - }; + return new Section(position); + } + + public Section appendSection(int maxByteCount, String name) { + int limit = fourByteAlign(length + maxByteCount); + Section result = new Section(name, length, limit); + length = limit; + return result; + } + + public void noMoreSections() { + data = new byte[length]; + } + + public int getLength() { + return length; + } + + public static int fourByteAlign(int position) { + return (position + 3) & ~3; + } + + public byte[] getBytes() { + return data; + } + + public List<String> strings() { + return strings; + } + + public List<Integer> typeIds() { + return typeIds; + } + + public List<String> typeNames() { + return typeNames; + } + + public List<ProtoId> protoIds() { + return protoIds; + } + + public List<FieldId> fieldIds() { + return fieldIds; + } + + public List<MethodId> methodIds() { + return methodIds; + } + + public Iterable<ClassDef> classDefs() { + return new Iterable<ClassDef>() { + @Override + public Iterator<ClassDef> iterator() { + if (!tableOfContents.classDefs.exists()) { + return Collections.<ClassDef>emptySet().iterator(); + } + return new Iterator<ClassDef>() { + private DexBuffer.Section in = open(tableOfContents.classDefs.off); + private int count = 0; + + @Override + public boolean hasNext() { + return count < tableOfContents.classDefs.size; + } + + @Override + public ClassDef next() { + if (!hasNext()) { + throw new NoSuchElementException(); } + count++; + return in.readClassDef(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } }; - } + } + }; + } - public TypeList readTypeList(int offset) { - if (offset == 0) { - return TypeList.EMPTY; - } - return open(offset).readTypeList(); + public TypeList readTypeList(int offset) { + if (offset == 0) { + return TypeList.EMPTY; } + return open(offset).readTypeList(); + } - public ClassData readClassData(ClassDef classDef) { - int offset = classDef.getClassDataOffset(); - if (offset == 0) { - throw new IllegalArgumentException("offset == 0"); - } - return open(offset).readClassData(); + public ClassData readClassData(ClassDef classDef) { + int offset = classDef.getClassDataOffset(); + if (offset == 0) { + throw new IllegalArgumentException("offset == 0"); } + return open(offset).readClassData(); + } - public Code readCode(ClassData.Method method) { - int offset = method.getCodeOffset(); - if (offset == 0) { - throw new IllegalArgumentException("offset == 0"); - } - return open(offset).readCode(); + public Code readCode(ClassData.Method method) { + int offset = method.getCodeOffset(); + if (offset == 0) { + throw new IllegalArgumentException("offset == 0"); + } + return open(offset).readCode(); + } + + /** + * TODO(jack team) + */ + public final class Section implements ByteInput, ByteOutput { + private final String name; + private int position; + private final int limit; + private final int initialPosition; + + private Section(String name, int position, int limit) { + this.name = name; + this.position = this.initialPosition = position; + this.limit = limit; } - public final class Section implements ByteInput, ByteOutput { - private final String name; - private int position; - private final int limit; - private final int initialPosition; - - private Section(String name, int position, int limit) { - this.name = name; - this.position = this.initialPosition = position; - this.limit = limit; - } - - private Section(int position) { - this("section", position, data.length); - } + private Section(int position) { + this("section", position, data.length); + } - public int getPosition() { - return position; - } + public int getPosition() { + return position; + } - public int readInt() { - int result = (data[position] & 0xff) - | (data[position + 1] & 0xff) << 8 - | (data[position + 2] & 0xff) << 16 - | (data[position + 3] & 0xff) << 24; - position += 4; - return result; - } + public int readInt() { + int result = (data[position] & 0xff) | (data[position + 1] & 0xff) << 8 + | (data[position + 2] & 0xff) << 16 | (data[position + 3] & 0xff) << 24; + position += 4; + return result; + } - public short readShort() { - int result = (data[position] & 0xff) - | (data[position + 1] & 0xff) << 8; - position += 2; - return (short) result; - } + public short readShort() { + int result = (data[position] & 0xff) | (data[position + 1] & 0xff) << 8; + position += 2; + return (short) result; + } - public int readUnsignedShort() { - return readShort() & 0xffff; - } + public int readUnsignedShort() { + return readShort() & 0xffff; + } - public byte readByte() { - return (byte) (data[position++] & 0xff); - } + @Override + public byte readByte() { + return (byte) (data[position++] & 0xff); + } - public byte[] readByteArray(int length) { - byte[] result = Arrays.copyOfRange(data, position, position + length); - position += length; - return result; - } + public byte[] readByteArray(int length) { + byte[] result = Arrays.copyOfRange(data, position, position + length); + position += length; + return result; + } - public short[] readShortArray(int length) { - short[] result = new short[length]; - for (int i = 0; i < length; i++) { - result[i] = readShort(); - } - return result; - } + public short[] readShortArray(int length) { + short[] result = new short[length]; + for (int i = 0; i < length; i++) { + result[i] = readShort(); + } + return result; + } - public int readUleb128() { - return Leb128Utils.readUnsignedLeb128(this); - } + public int readUleb128() { + return Leb128Utils.readUnsignedLeb128(this); + } - public int readUleb128p1() { - return Leb128Utils.readUnsignedLeb128(this) - 1; - } + public int readUleb128p1() { + return Leb128Utils.readUnsignedLeb128(this) - 1; + } - public int readSleb128() { - return Leb128Utils.readSignedLeb128(this); - } + public int readSleb128() { + return Leb128Utils.readSignedLeb128(this); + } - public TypeList readTypeList() { - assertFourByteAligned(); - int size = readInt(); - short[] types = new short[size]; - for (int i = 0; i < size; i++) { - types[i] = readShort(); - } - position = DexBuffer.fourByteAlign(position); - return new TypeList(DexBuffer.this, types); - } + public TypeList readTypeList() { + assertFourByteAligned(); + int size = readInt(); + short[] types = new short[size]; + for (int i = 0; i < size; i++) { + types[i] = readShort(); + } + position = DexBuffer.fourByteAlign(position); + return new TypeList(DexBuffer.this, types); + } - public String readString() { - int offset = readInt(); - int savedPosition = position; - position = offset; - try { - int expectedLength = readUleb128(); - String result = Mutf8.decode(this, new char[expectedLength]); - if (result.length() != expectedLength) { - throw new DexException("Declared length " + expectedLength - + " doesn't match decoded length of " + result.length()); - } - return result; - } catch (UTFDataFormatException e) { - throw new DexException(e); - } finally { - position = savedPosition; - } + public String readString() { + int offset = readInt(); + int savedPosition = position; + position = offset; + try { + int expectedLength = readUleb128(); + String result = Mutf8.decode(this, new char[expectedLength]); + if (result.length() != expectedLength) { + throw new DexException("Declared length " + expectedLength + + " doesn't match decoded length of " + result.length()); } + return result; + } catch (UTFDataFormatException e) { + throw new DexException(e); + } finally { + position = savedPosition; + } + } - public FieldId readFieldId() { - int declaringClassIndex = readUnsignedShort(); - int typeIndex = readUnsignedShort(); - int nameIndex = readInt(); - return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex); - } + public FieldId readFieldId() { + int declaringClassIndex = readUnsignedShort(); + int typeIndex = readUnsignedShort(); + int nameIndex = readInt(); + return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex); + } - public MethodId readMethodId() { - int declaringClassIndex = readUnsignedShort(); - int protoIndex = readUnsignedShort(); - int nameIndex = readInt(); - return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex); - } + public MethodId readMethodId() { + int declaringClassIndex = readUnsignedShort(); + int protoIndex = readUnsignedShort(); + int nameIndex = readInt(); + return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex); + } - public ProtoId readProtoId() { - int shortyIndex = readInt(); - int returnTypeIndex = readInt(); - int parametersOffset = readInt(); - return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset); - } + public ProtoId readProtoId() { + int shortyIndex = readInt(); + int returnTypeIndex = readInt(); + int parametersOffset = readInt(); + return new ProtoId(DexBuffer.this, shortyIndex, returnTypeIndex, parametersOffset); + } - public ClassDef readClassDef() { - int offset = getPosition(); - int type = readInt(); - int accessFlags = readInt(); - int supertype = readInt(); - int interfacesOffset = readInt(); - int sourceFileIndex = readInt(); - int annotationsOffset = readInt(); - int classDataOffset = readInt(); - int staticValuesOffset = readInt(); - return new ClassDef(DexBuffer.this, offset, type, accessFlags, supertype, - interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset, - staticValuesOffset); - } + public ClassDef readClassDef() { + int offset = getPosition(); + int type = readInt(); + int accessFlags = readInt(); + int supertype = readInt(); + int interfacesOffset = readInt(); + int sourceFileIndex = readInt(); + int annotationsOffset = readInt(); + int classDataOffset = readInt(); + int staticValuesOffset = readInt(); + return new ClassDef(DexBuffer.this, + offset, + type, + accessFlags, + supertype, + interfacesOffset, + sourceFileIndex, + annotationsOffset, + classDataOffset, + staticValuesOffset); + } - private Code readCode() { - int registersSize = readUnsignedShort(); - int insSize = readUnsignedShort(); - int outsSize = readUnsignedShort(); - int triesSize = readUnsignedShort(); - int debugInfoOffset = readInt(); - int instructionsSize = readInt(); - short[] instructions = readShortArray(instructionsSize); - Try[] tries; - CatchHandler[] catchHandlers; - if (triesSize > 0) { - if (instructions.length % 2 == 1) { - readShort(); // padding - } - - /* - * We can't read the tries until we've read the catch handlers. - * Unfortunately they're in the opposite order in the dex file - * so we need to read them out-of-order. - */ - Section triesSection = open(position); - skip(triesSize * SizeOf.TRY_ITEM); - catchHandlers = readCatchHandlers(); - tries = triesSection.readTries(triesSize, catchHandlers); - } else { - tries = new Try[0]; - catchHandlers = new CatchHandler[0]; - } - return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions, - tries, catchHandlers); - } + private Code readCode() { + int registersSize = readUnsignedShort(); + int insSize = readUnsignedShort(); + int outsSize = readUnsignedShort(); + int triesSize = readUnsignedShort(); + int debugInfoOffset = readInt(); + int instructionsSize = readInt(); + short[] instructions = readShortArray(instructionsSize); + Try[] tries; + CatchHandler[] catchHandlers; + if (triesSize > 0) { + if (instructions.length % 2 == 1) { + readShort(); // padding + } + + /* + * We can't read the tries until we've read the catch handlers. + * Unfortunately they're in the opposite order in the dex file + * so we need to read them out-of-order. + */ + Section triesSection = open(position); + skip(triesSize * SizeOf.TRY_ITEM); + catchHandlers = readCatchHandlers(); + tries = triesSection.readTries(triesSize, catchHandlers); + } else { + tries = new Try[0]; + catchHandlers = new CatchHandler[0]; + } + return new Code(registersSize, + insSize, + outsSize, + debugInfoOffset, + instructions, + tries, + catchHandlers); + } - private CatchHandler[] readCatchHandlers() { - int baseOffset = position; - int catchHandlersSize = readUleb128(); - CatchHandler[] result = new CatchHandler[catchHandlersSize]; - for (int i = 0; i < catchHandlersSize; i++) { - int offset = position - baseOffset; - result[i] = readCatchHandler(offset); - } - return result; - } + private CatchHandler[] readCatchHandlers() { + int baseOffset = position; + int catchHandlersSize = readUleb128(); + CatchHandler[] result = new CatchHandler[catchHandlersSize]; + for (int i = 0; i < catchHandlersSize; i++) { + int offset = position - baseOffset; + result[i] = readCatchHandler(offset); + } + return result; + } - private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) { - Try[] result = new Try[triesSize]; - for (int i = 0; i < triesSize; i++) { - int startAddress = readInt(); - int instructionCount = readUnsignedShort(); - int handlerOffset = readUnsignedShort(); - int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset); - result[i] = new Try(startAddress, instructionCount, catchHandlerIndex); - } - return result; - } + private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) { + Try[] result = new Try[triesSize]; + for (int i = 0; i < triesSize; i++) { + int startAddress = readInt(); + int instructionCount = readUnsignedShort(); + int handlerOffset = readUnsignedShort(); + int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset); + result[i] = new Try(startAddress, instructionCount, catchHandlerIndex); + } + return result; + } - private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) { - for (int i = 0; i < catchHandlers.length; i++) { - CatchHandler catchHandler = catchHandlers[i]; - if (catchHandler.getOffset() == offset) { - return i; - } - } - throw new IllegalArgumentException(); + private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) { + for (int i = 0; i < catchHandlers.length; i++) { + CatchHandler catchHandler = catchHandlers[i]; + if (catchHandler.getOffset() == offset) { + return i; } + } + throw new IllegalArgumentException(); + } - private CatchHandler readCatchHandler(int offset) { - int size = readSleb128(); - int handlersCount = Math.abs(size); - int[] typeIndexes = new int[handlersCount]; - int[] addresses = new int[handlersCount]; - for (int i = 0; i < handlersCount; i++) { - typeIndexes[i] = readUleb128(); - addresses[i] = readUleb128(); - } - int catchAllAddress = size <= 0 ? readUleb128() : -1; - return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset); - } + private CatchHandler readCatchHandler(int offset) { + int size = readSleb128(); + int handlersCount = Math.abs(size); + int[] typeIndexes = new int[handlersCount]; + int[] addresses = new int[handlersCount]; + for (int i = 0; i < handlersCount; i++) { + typeIndexes[i] = readUleb128(); + addresses[i] = readUleb128(); + } + int catchAllAddress = size <= 0 ? readUleb128() : -1; + return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset); + } - private ClassData readClassData() { - int staticFieldsSize = readUleb128(); - int instanceFieldsSize = readUleb128(); - int directMethodsSize = readUleb128(); - int virtualMethodsSize = readUleb128(); - ClassData.Field[] staticFields = readFields(staticFieldsSize); - ClassData.Field[] instanceFields = readFields(instanceFieldsSize); - ClassData.Method[] directMethods = readMethods(directMethodsSize); - ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); - return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); - } + private ClassData readClassData() { + int staticFieldsSize = readUleb128(); + int instanceFieldsSize = readUleb128(); + int directMethodsSize = readUleb128(); + int virtualMethodsSize = readUleb128(); + ClassData.Field[] staticFields = readFields(staticFieldsSize); + ClassData.Field[] instanceFields = readFields(instanceFieldsSize); + ClassData.Method[] directMethods = readMethods(directMethodsSize); + ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize); + return new ClassData(staticFields, instanceFields, directMethods, virtualMethods); + } - private ClassData.Field[] readFields(int count) { - ClassData.Field[] result = new ClassData.Field[count]; - int fieldIndex = 0; - for (int i = 0; i < count; i++) { - fieldIndex += readUleb128(); // field index diff - int accessFlags = readUleb128(); - result[i] = new ClassData.Field(fieldIndex, accessFlags); - } - return result; - } + private ClassData.Field[] readFields(int count) { + ClassData.Field[] result = new ClassData.Field[count]; + int fieldIndex = 0; + for (int i = 0; i < count; i++) { + fieldIndex += readUleb128(); // field index diff + int accessFlags = readUleb128(); + result[i] = new ClassData.Field(fieldIndex, accessFlags); + } + return result; + } - private ClassData.Method[] readMethods(int count) { - ClassData.Method[] result = new ClassData.Method[count]; - int methodIndex = 0; - for (int i = 0; i < count; i++) { - methodIndex += readUleb128(); // method index diff - int accessFlags = readUleb128(); - int codeOff = readUleb128(); - result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); - } - return result; - } + private ClassData.Method[] readMethods(int count) { + ClassData.Method[] result = new ClassData.Method[count]; + int methodIndex = 0; + for (int i = 0; i < count; i++) { + methodIndex += readUleb128(); // method index diff + int accessFlags = readUleb128(); + int codeOff = readUleb128(); + result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff); + } + return result; + } - public Annotation readAnnotation() { - byte visibility = readByte(); - int typeIndex = readUleb128(); - int size = readUleb128(); - int[] names = new int[size]; - EncodedValue[] values = new EncodedValue[size]; - for (int i = 0; i < size; i++) { - names[i] = readUleb128(); - values[i] = readEncodedValue(); - } - return new Annotation(DexBuffer.this, visibility, typeIndex, names, values); - } + public Annotation readAnnotation() { + byte visibility = readByte(); + int typeIndex = readUleb128(); + int size = readUleb128(); + int[] names = new int[size]; + EncodedValue[] values = new EncodedValue[size]; + for (int i = 0; i < size; i++) { + names[i] = readUleb128(); + values[i] = readEncodedValue(); + } + return new Annotation(DexBuffer.this, visibility, typeIndex, names, values); + } - public EncodedValue readEncodedValue() { - int start = position; - new EncodedValueReader(this).readValue(); - int end = position; - return new EncodedValue(Arrays.copyOfRange(data, start, end)); - } + public EncodedValue readEncodedValue() { + int start = position; + new EncodedValueReader(this).readValue(); + int end = position; + return new EncodedValue(Arrays.copyOfRange(data, start, end)); + } - public EncodedValue readEncodedArray() { - int start = position; - new EncodedValueReader(this).readArray(); - int end = position; - return new EncodedValue(Arrays.copyOfRange(data, start, end)); - } + public EncodedValue readEncodedArray() { + int start = position; + new EncodedValueReader(this).readArray(); + int end = position; + return new EncodedValue(Arrays.copyOfRange(data, start, end)); + } - private void ensureCapacity(int size) { - if (position + size > limit) { - throw new DexException("Section limit " + limit + " exceeded by " + name); - } - } + private void ensureCapacity(int size) { + if (position + size > limit) { + throw new DexException("Section limit " + limit + " exceeded by " + name); + } + } - public void skip(int count) { - if (count < 0) { - throw new IllegalArgumentException(); - } - ensureCapacity(count); - position += count; - } + public void skip(int count) { + if (count < 0) { + throw new IllegalArgumentException(); + } + ensureCapacity(count); + position += count; + } - /** - * Writes 0x00 until the position is aligned to a multiple of 4. - */ - public void alignToFourBytes() { - int unalignedCount = position; - position = DexBuffer.fourByteAlign(position); - for (int i = unalignedCount; i < position; i++) { - data[i] = 0; - } - } + /** + * Writes 0x00 until the position is aligned to a multiple of 4. + */ + public void alignToFourBytes() { + int unalignedCount = position; + position = DexBuffer.fourByteAlign(position); + for (int i = unalignedCount; i < position; i++) { + data[i] = 0; + } + } - public void assertFourByteAligned() { - if ((position & 3) != 0) { - throw new IllegalStateException("Not four byte aligned!"); - } - } + public void assertFourByteAligned() { + if ((position & 3) != 0) { + throw new IllegalStateException("Not four byte aligned!"); + } + } - public void write(byte[] bytes) { - ensureCapacity(bytes.length); - System.arraycopy(bytes, 0, data, position, bytes.length); - position += bytes.length; - } + public void write(byte[] bytes) { + ensureCapacity(bytes.length); + System.arraycopy(bytes, 0, data, position, bytes.length); + position += bytes.length; + } - public void writeByte(int b) { - ensureCapacity(1); - data[position++] = (byte) b; - } + @Override + public void writeByte(int b) { + ensureCapacity(1); + data[position++] = (byte) b; + } - public void writeShort(short i) { - ensureCapacity(2); - data[position ] = (byte) i; - data[position + 1] = (byte) (i >>> 8); - position += 2; - } + public void writeShort(short i) { + ensureCapacity(2); + data[position] = (byte) i; + data[position + 1] = (byte) (i >>> 8); + position += 2; + } - public void writeUnsignedShort(int i) { - short s = (short) i; - if (i != (s & 0xffff)) { - throw new IllegalArgumentException("Expected an unsigned short: " + i); - } - writeShort(s); - } + public void writeUnsignedShort(int i) { + short s = (short) i; + if (i != (s & 0xffff)) { + throw new IllegalArgumentException("Expected an unsigned short: " + i); + } + writeShort(s); + } - public void write(short[] shorts) { - for (short s : shorts) { - writeShort(s); - } - } + public void write(short[] shorts) { + for (short s : shorts) { + writeShort(s); + } + } - public void writeInt(int i) { - ensureCapacity(4); - data[position ] = (byte) i; - data[position + 1] = (byte) (i >>> 8); - data[position + 2] = (byte) (i >>> 16); - data[position + 3] = (byte) (i >>> 24); - position += 4; - } + public void writeInt(int i) { + ensureCapacity(4); + data[position] = (byte) i; + data[position + 1] = (byte) (i >>> 8); + data[position + 2] = (byte) (i >>> 16); + data[position + 3] = (byte) (i >>> 24); + position += 4; + } - public void writeUleb128(int i) { - try { - Leb128Utils.writeUnsignedLeb128(this, i); - ensureCapacity(0); - } catch (ArrayIndexOutOfBoundsException e) { - throw new DexException("Section limit " + limit + " exceeded by " + name); - } - } + public void writeUleb128(int i) { + try { + Leb128Utils.writeUnsignedLeb128(this, i); + ensureCapacity(0); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DexException("Section limit " + limit + " exceeded by " + name); + } + } - public void writeUleb128p1(int i) { - writeUleb128(i + 1); - } + public void writeUleb128p1(int i) { + writeUleb128(i + 1); + } - public void writeSleb128(int i) { - try { - Leb128Utils.writeSignedLeb128(this, i); - ensureCapacity(0); - } catch (ArrayIndexOutOfBoundsException e) { - throw new DexException("Section limit " + limit + " exceeded by " + name); - } - } + public void writeSleb128(int i) { + try { + Leb128Utils.writeSignedLeb128(this, i); + ensureCapacity(0); + } catch (ArrayIndexOutOfBoundsException e) { + throw new DexException("Section limit " + limit + " exceeded by " + name); + } + } - public void writeStringData(String value) { - try { - int length = value.length(); - writeUleb128(length); - write(Mutf8.encode(value)); - writeByte(0); - } catch (UTFDataFormatException e) { - throw new AssertionError(); - } - } + public void writeStringData(String value) { + try { + int length = value.length(); + writeUleb128(length); + write(Mutf8.encode(value)); + writeByte(0); + } catch (UTFDataFormatException e) { + throw new AssertionError(); + } + } - public void writeTypeList(TypeList typeList) { - short[] types = typeList.getTypes(); - writeInt(types.length); - for (short type : types) { - writeShort(type); - } - alignToFourBytes(); - } + public void writeTypeList(TypeList typeList) { + short[] types = typeList.getTypes(); + writeInt(types.length); + for (short type : types) { + writeShort(type); + } + alignToFourBytes(); + } - /** - * Returns the number of bytes remaining in this section. - */ - public int remaining() { - return limit - position; - } + /** + * Returns the number of bytes remaining in this section. + */ + public int remaining() { + return limit - position; + } - /** - * Returns the number of bytes used by this section. - */ - public int used () { - return position - initialPosition; - } + /** + * Returns the number of bytes used by this section. + */ + public int used() { + return position - initialPosition; } + } } diff --git a/dx/src/com/android/jack/dx/io/DexHasher.java b/dx/src/com/android/jack/dx/io/DexHasher.java index 301dd37..3a9b3ba 100644 --- a/dx/src/com/android/jack/dx/io/DexHasher.java +++ b/dx/src/com/android/jack/dx/io/DexHasher.java @@ -25,51 +25,51 @@ import java.util.zip.Adler32; * Generates and stores the checksum and signature of a dex file. */ public final class DexHasher { - private static final int CHECKSUM_OFFSET = 8; - private static final int CHECKSUM_SIZE = 4; - private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE; - private static final int SIGNATURE_SIZE = 20; + private static final int CHECKSUM_OFFSET = 8; + private static final int CHECKSUM_SIZE = 4; + private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE; + private static final int SIGNATURE_SIZE = 20; - /** - * Returns the signature of all but the first 32 bytes of {@code dex}. The - * first 32 bytes of dex files are not specified to be included in the - * signature. - */ - public byte[] computeSignature(DexBuffer dex) throws IOException { - MessageDigest digest; - try { - digest = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(); - } - int offset = SIGNATURE_OFFSET + SIGNATURE_SIZE; - - byte[] bytes = dex.getBytes(); - digest.update(bytes, offset, bytes.length - offset); - return digest.digest(); + /** + * Returns the signature of all but the first 32 bytes of {@code dex}. The + * first 32 bytes of dex files are not specified to be included in the + * signature. + */ + public byte[] computeSignature(DexBuffer dex) throws IOException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(); } + int offset = SIGNATURE_OFFSET + SIGNATURE_SIZE; - /** - * Returns the checksum of all but the first 12 bytes of {@code dex}. - */ - public int computeChecksum(DexBuffer dex) throws IOException { - Adler32 adler32 = new Adler32(); - int offset = CHECKSUM_OFFSET + CHECKSUM_SIZE; + byte[] bytes = dex.getBytes(); + digest.update(bytes, offset, bytes.length - offset); + return digest.digest(); + } - byte[] bytes = dex.getBytes(); - adler32.update(bytes, offset, bytes.length - offset); - return (int) adler32.getValue(); - } + /** + * Returns the checksum of all but the first 12 bytes of {@code dex}. + */ + public int computeChecksum(DexBuffer dex) throws IOException { + Adler32 adler32 = new Adler32(); + int offset = CHECKSUM_OFFSET + CHECKSUM_SIZE; - /** - * Generates the signature and checksum of the dex file {@code out} and - * writes them to the file. - */ - public void writeHashes(DexBuffer dex) throws IOException { - byte[] signature = computeSignature(dex); - dex.open(SIGNATURE_OFFSET).write(signature); + byte[] bytes = dex.getBytes(); + adler32.update(bytes, offset, bytes.length - offset); + return (int) adler32.getValue(); + } - int checksum = computeChecksum(dex); - dex.open(CHECKSUM_OFFSET).writeInt(checksum); - } + /** + * Generates the signature and checksum of the dex file {@code out} and + * writes them to the file. + */ + public void writeHashes(DexBuffer dex) throws IOException { + byte[] signature = computeSignature(dex); + dex.open(SIGNATURE_OFFSET).write(signature); + + int checksum = computeChecksum(dex); + dex.open(CHECKSUM_OFFSET).writeInt(checksum); + } } diff --git a/dx/src/com/android/jack/dx/io/DexIndexPrinter.java b/dx/src/com/android/jack/dx/io/DexIndexPrinter.java index 952649c..89dc385 100644 --- a/dx/src/com/android/jack/dx/io/DexIndexPrinter.java +++ b/dx/src/com/android/jack/dx/io/DexIndexPrinter.java @@ -25,101 +25,100 @@ import java.io.IOException; * Executable that prints all indices of a dex file. */ public final class DexIndexPrinter { - private final DexBuffer dexBuffer; - private final TableOfContents tableOfContents; + private final DexBuffer dexBuffer; + private final TableOfContents tableOfContents; - public DexIndexPrinter(File file) throws IOException { - this.dexBuffer = new DexBuffer(file); - this.tableOfContents = dexBuffer.getTableOfContents(); - } + public DexIndexPrinter(File file) throws IOException { + this.dexBuffer = new DexBuffer(file); + this.tableOfContents = dexBuffer.getTableOfContents(); + } - private void printMap() { - for (TableOfContents.Section section : tableOfContents.sections) { - if (section.off != -1) { - System.out.println("section " + Integer.toHexString(section.type) - + " off=" + Integer.toHexString(section.off) - + " size=" + Integer.toHexString(section.size) - + " byteCount=" + Integer.toHexString(section.byteCount)); - } - } + private void printMap() { + for (TableOfContents.Section section : tableOfContents.sections) { + if (section.off != -1) { + System.out.println("section " + Integer.toHexString(section.type) + " off=" + + Integer.toHexString(section.off) + " size=" + Integer.toHexString(section.size) + + " byteCount=" + Integer.toHexString(section.byteCount)); + } } + } - private void printStrings() throws IOException { - int index = 0; - for (String string : dexBuffer.strings()) { - System.out.println("string " + index + ": " + string); - index++; - } + private void printStrings() throws IOException { + int index = 0; + for (String string : dexBuffer.strings()) { + System.out.println("string " + index + ": " + string); + index++; } + } - private void printTypeIds() throws IOException { - int index = 0; - for (Integer type : dexBuffer.typeIds()) { - System.out.println("type " + index + ": " + dexBuffer.strings().get(type)); - index++; - } + private void printTypeIds() throws IOException { + int index = 0; + for (Integer type : dexBuffer.typeIds()) { + System.out.println("type " + index + ": " + dexBuffer.strings().get(type)); + index++; } + } - private void printProtoIds() throws IOException { - int index = 0; - for (ProtoId protoId : dexBuffer.protoIds()) { - System.out.println("proto " + index + ": " + protoId); - index++; - } + private void printProtoIds() throws IOException { + int index = 0; + for (ProtoId protoId : dexBuffer.protoIds()) { + System.out.println("proto " + index + ": " + protoId); + index++; } + } - private void printFieldIds() throws IOException { - int index = 0; - for (FieldId fieldId : dexBuffer.fieldIds()) { - System.out.println("field " + index + ": " + fieldId); - index++; - } + private void printFieldIds() throws IOException { + int index = 0; + for (FieldId fieldId : dexBuffer.fieldIds()) { + System.out.println("field " + index + ": " + fieldId); + index++; } + } - private void printMethodIds() throws IOException { - int index = 0; - for (MethodId methodId : dexBuffer.methodIds()) { - System.out.println("methodId " + index + ": " + methodId); - index++; - } + private void printMethodIds() throws IOException { + int index = 0; + for (MethodId methodId : dexBuffer.methodIds()) { + System.out.println("methodId " + index + ": " + methodId); + index++; } + } - private void printTypeLists() throws IOException { - if (tableOfContents.typeLists.off == -1) { - System.out.println("No type lists"); - return; - } - DexBuffer.Section in = dexBuffer.open(tableOfContents.typeLists.off); - for (int i = 0; i < tableOfContents.typeLists.size; i++) { - int size = in.readInt(); - System.out.print("Type list i=" + i + ", size=" + size + ", elements="); - for (int t = 0; t < size; t++) { - System.out.print(" " + dexBuffer.typeNames().get((int) in.readShort())); - } - if (size % 2 == 1) { - in.readShort(); // retain alignment - } - System.out.println(); - } + private void printTypeLists() throws IOException { + if (tableOfContents.typeLists.off == -1) { + System.out.println("No type lists"); + return; } - - private void printClassDefs() { - int index = 0; - for (ClassDef classDef : dexBuffer.classDefs()) { - System.out.println("class def " + index + ": " + classDef); - index++; - } + DexBuffer.Section in = dexBuffer.open(tableOfContents.typeLists.off); + for (int i = 0; i < tableOfContents.typeLists.size; i++) { + int size = in.readInt(); + System.out.print("Type list i=" + i + ", size=" + size + ", elements="); + for (int t = 0; t < size; t++) { + System.out.print(" " + dexBuffer.typeNames().get(in.readShort())); + } + if (size % 2 == 1) { + in.readShort(); // retain alignment + } + System.out.println(); } + } - public static void main(String[] args) throws IOException { - DexIndexPrinter indexPrinter = new DexIndexPrinter(new File(args[0])); - indexPrinter.printMap(); - indexPrinter.printStrings(); - indexPrinter.printTypeIds(); - indexPrinter.printProtoIds(); - indexPrinter.printFieldIds(); - indexPrinter.printMethodIds(); - indexPrinter.printTypeLists(); - indexPrinter.printClassDefs(); + private void printClassDefs() { + int index = 0; + for (ClassDef classDef : dexBuffer.classDefs()) { + System.out.println("class def " + index + ": " + classDef); + index++; } + } + + public static void main(String[] args) throws IOException { + DexIndexPrinter indexPrinter = new DexIndexPrinter(new File(args[0])); + indexPrinter.printMap(); + indexPrinter.printStrings(); + indexPrinter.printTypeIds(); + indexPrinter.printProtoIds(); + indexPrinter.printFieldIds(); + indexPrinter.printMethodIds(); + indexPrinter.printTypeLists(); + indexPrinter.printClassDefs(); + } } diff --git a/dx/src/com/android/jack/dx/io/EncodedValue.java b/dx/src/com/android/jack/dx/io/EncodedValue.java index 861dfcf..604c80c 100644 --- a/dx/src/com/android/jack/dx/io/EncodedValue.java +++ b/dx/src/com/android/jack/dx/io/EncodedValue.java @@ -23,35 +23,37 @@ import com.android.jack.dx.util.ByteInput; * An encoded value or array. */ public final class EncodedValue implements Comparable<EncodedValue> { - private final byte[] data; - - public EncodedValue(byte[] data) { - this.data = data; - } - - public ByteInput asByteInput() { - return new ByteArrayByteInput(data); - } - - public byte[] getBytes() { - return data; + private final byte[] data; + + public EncodedValue(byte[] data) { + this.data = data; + } + + public ByteInput asByteInput() { + return new ByteArrayByteInput(data); + } + + public byte[] getBytes() { + return data; + } + + public void writeTo(DexBuffer.Section out) { + out.write(data); + } + + @Override + public int compareTo(EncodedValue other) { + int size = Math.min(data.length, other.data.length); + for (int i = 0; i < size; i++) { + if (data[i] != other.data[i]) { + return (data[i] & 0xff) - (other.data[i] & 0xff); + } } + return data.length - other.data.length; + } - public void writeTo(DexBuffer.Section out) { - out.write(data); - } - - @Override public int compareTo(EncodedValue other) { - int size = Math.min(data.length, other.data.length); - for (int i = 0; i < size; i++) { - if (data[i] != other.data[i]) { - return (data[i] & 0xff) - (other.data[i] & 0xff); - } - } - return data.length - other.data.length; - } - - @Override public String toString() { - return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")"; - } + @Override + public String toString() { + return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")"; + } } diff --git a/dx/src/com/android/jack/dx/io/EncodedValueReader.java b/dx/src/com/android/jack/dx/io/EncodedValueReader.java index 694d736..d7eade0 100644 --- a/dx/src/com/android/jack/dx/io/EncodedValueReader.java +++ b/dx/src/com/android/jack/dx/io/EncodedValueReader.java @@ -21,126 +21,137 @@ import com.android.jack.dx.util.Leb128Utils; /** * SAX-style reader for encoded values. - * TODO: convert this to a pull-style reader + * TODO(dx team): convert this to a pull-style reader */ public class EncodedValueReader { - public static final int ENCODED_BYTE = 0x00; - public static final int ENCODED_SHORT = 0x02; - public static final int ENCODED_CHAR = 0x03; - public static final int ENCODED_INT = 0x04; - public static final int ENCODED_LONG = 0x06; - public static final int ENCODED_FLOAT = 0x10; - public static final int ENCODED_DOUBLE = 0x11; - public static final int ENCODED_STRING = 0x17; - public static final int ENCODED_TYPE = 0x18; - public static final int ENCODED_FIELD = 0x19; - public static final int ENCODED_ENUM = 0x1b; - public static final int ENCODED_METHOD = 0x1a; - public static final int ENCODED_ARRAY = 0x1c; - public static final int ENCODED_ANNOTATION = 0x1d; - public static final int ENCODED_NULL = 0x1e; - public static final int ENCODED_BOOLEAN = 0x1f; - - protected final ByteInput in; - - public EncodedValueReader(ByteInput in) { - this.in = in; - } + public static final int ENCODED_BYTE = 0x00; + public static final int ENCODED_SHORT = 0x02; + public static final int ENCODED_CHAR = 0x03; + public static final int ENCODED_INT = 0x04; + public static final int ENCODED_LONG = 0x06; + public static final int ENCODED_FLOAT = 0x10; + public static final int ENCODED_DOUBLE = 0x11; + public static final int ENCODED_STRING = 0x17; + public static final int ENCODED_TYPE = 0x18; + public static final int ENCODED_FIELD = 0x19; + public static final int ENCODED_ENUM = 0x1b; + public static final int ENCODED_METHOD = 0x1a; + public static final int ENCODED_ARRAY = 0x1c; + public static final int ENCODED_ANNOTATION = 0x1d; + public static final int ENCODED_NULL = 0x1e; + public static final int ENCODED_BOOLEAN = 0x1f; - public EncodedValueReader(EncodedValue in) { - this(in.asByteInput()); - } + protected final ByteInput in; + + public EncodedValueReader(ByteInput in) { + this.in = in; + } - public final void readArray() { - int size = Leb128Utils.readUnsignedLeb128(in); - visitArray(size); + public EncodedValueReader(EncodedValue in) { + this(in.asByteInput()); + } - for (int i = 0; i < size; i++) { - readValue(); - } + public final void readArray() { + int size = Leb128Utils.readUnsignedLeb128(in); + visitArray(size); + + for (int i = 0; i < size; i++) { + readValue(); } + } - public final void readAnnotation() { - int typeIndex = Leb128Utils.readUnsignedLeb128(in); - int size = Leb128Utils.readUnsignedLeb128(in); - visitAnnotation(typeIndex, size); + public final void readAnnotation() { + int typeIndex = Leb128Utils.readUnsignedLeb128(in); + int size = Leb128Utils.readUnsignedLeb128(in); + visitAnnotation(typeIndex, size); - for (int i = 0; i < size; i++) { - visitAnnotationName(Leb128Utils.readUnsignedLeb128(in)); - readValue(); - } + for (int i = 0; i < size; i++) { + visitAnnotationName(Leb128Utils.readUnsignedLeb128(in)); + readValue(); } + } + + public final void readValue() { + int argAndType = in.readByte() & 0xff; + int type = argAndType & 0x1f; + int arg = (argAndType & 0xe0) >> 5; + int size = arg + 1; - public final void readValue() { - int argAndType = in.readByte() & 0xff; - int type = argAndType & 0x1f; - int arg = (argAndType & 0xe0) >> 5; - int size = arg + 1; - - switch (type) { - case ENCODED_BYTE: - case ENCODED_SHORT: - case ENCODED_CHAR: - case ENCODED_INT: - case ENCODED_LONG: - case ENCODED_FLOAT: - case ENCODED_DOUBLE: - visitPrimitive(argAndType, type, arg, size); - break; - case ENCODED_STRING: - visitString(type, readIndex(in, size)); - break; - case ENCODED_TYPE: - visitType(type, readIndex(in, size)); - break; - case ENCODED_FIELD: - case ENCODED_ENUM: - visitField(type, readIndex(in, size)); - break; - case ENCODED_METHOD: - visitMethod(type, readIndex(in, size)); - break; - case ENCODED_ARRAY: - visitArrayValue(argAndType); - readArray(); - break; - case ENCODED_ANNOTATION: - visitAnnotationValue(argAndType); - readAnnotation(); - break; - case ENCODED_NULL: - visitEncodedNull(argAndType); - break; - case ENCODED_BOOLEAN: - visitEncodedBoolean(argAndType); - break; - } + switch (type) { + case ENCODED_BYTE: + case ENCODED_SHORT: + case ENCODED_CHAR: + case ENCODED_INT: + case ENCODED_LONG: + case ENCODED_FLOAT: + case ENCODED_DOUBLE: + visitPrimitive(argAndType, type, arg, size); + break; + case ENCODED_STRING: + visitString(type, readIndex(in, size)); + break; + case ENCODED_TYPE: + visitType(type, readIndex(in, size)); + break; + case ENCODED_FIELD: + case ENCODED_ENUM: + visitField(type, readIndex(in, size)); + break; + case ENCODED_METHOD: + visitMethod(type, readIndex(in, size)); + break; + case ENCODED_ARRAY: + visitArrayValue(argAndType); + readArray(); + break; + case ENCODED_ANNOTATION: + visitAnnotationValue(argAndType); + readAnnotation(); + break; + case ENCODED_NULL: + visitEncodedNull(argAndType); + break; + case ENCODED_BOOLEAN: + visitEncodedBoolean(argAndType); + break; } + } - protected void visitArray(int size) {} - protected void visitAnnotation(int typeIndex, int size) {} - protected void visitAnnotationName(int nameIndex) {} - protected void visitPrimitive(int argAndType, int type, int arg, int size) { - for (int i = 0; i < size; i++) { - in.readByte(); - } + protected void visitArray(int size) {} + + protected void visitAnnotation(int typeIndex, int size) {} + + protected void visitAnnotationName(int nameIndex) {} + + protected void visitPrimitive(int argAndType, int type, int arg, int size) { + for (int i = 0; i < size; i++) { + in.readByte(); } - protected void visitString(int type, int index) {} - protected void visitType(int type, int index) {} - protected void visitField(int type, int index) {} - protected void visitMethod(int type, int index) {} - protected void visitArrayValue(int argAndType) {} - protected void visitAnnotationValue(int argAndType) {} - protected void visitEncodedBoolean(int argAndType) {} - protected void visitEncodedNull(int argAndType) {} - - private int readIndex(ByteInput in, int byteCount) { - int result = 0; - int shift = 0; - for (int i = 0; i < byteCount; i++) { - result += (in.readByte() & 0xff) << shift; - shift += 8; - } - return result; + } + + protected void visitString(int type, int index) {} + + protected void visitType(int type, int index) {} + + protected void visitField(int type, int index) {} + + protected void visitMethod(int type, int index) {} + + protected void visitArrayValue(int argAndType) {} + + protected void visitAnnotationValue(int argAndType) {} + + protected void visitEncodedBoolean(int argAndType) {} + + protected void visitEncodedNull(int argAndType) {} + + private int readIndex(ByteInput in, int byteCount) { + int result = 0; + int shift = 0; + for (int i = 0; i < byteCount; i++) { + result += (in.readByte() & 0xff) << shift; + shift += 8; } + return result; + } } diff --git a/dx/src/com/android/jack/dx/io/FieldId.java b/dx/src/com/android/jack/dx/io/FieldId.java index 73c749c..a0802a3 100644 --- a/dx/src/com/android/jack/dx/io/FieldId.java +++ b/dx/src/com/android/jack/dx/io/FieldId.java @@ -18,51 +18,56 @@ package com.android.jack.dx.io; import com.android.jack.dx.util.Unsigned; +/** + * TODO(jack team) + */ public final class FieldId implements Comparable<FieldId> { - private final DexBuffer buffer; - private final int declaringClassIndex; - private final int typeIndex; - private final int nameIndex; + private final DexBuffer buffer; + private final int declaringClassIndex; + private final int typeIndex; + private final int nameIndex; - public FieldId(DexBuffer buffer, int declaringClassIndex, int typeIndex, int nameIndex) { - this.buffer = buffer; - this.declaringClassIndex = declaringClassIndex; - this.typeIndex = typeIndex; - this.nameIndex = nameIndex; - } + public FieldId(DexBuffer buffer, int declaringClassIndex, int typeIndex, int nameIndex) { + this.buffer = buffer; + this.declaringClassIndex = declaringClassIndex; + this.typeIndex = typeIndex; + this.nameIndex = nameIndex; + } - public int getDeclaringClassIndex() { - return declaringClassIndex; - } + public int getDeclaringClassIndex() { + return declaringClassIndex; + } - public int getTypeIndex() { - return typeIndex; - } + public int getTypeIndex() { + return typeIndex; + } - public int getNameIndex() { - return nameIndex; - } + public int getNameIndex() { + return nameIndex; + } - public int compareTo(FieldId other) { - if (declaringClassIndex != other.declaringClassIndex) { - return Unsigned.compare(declaringClassIndex, other.declaringClassIndex); - } - if (nameIndex != other.nameIndex) { - return Unsigned.compare(nameIndex, other.nameIndex); - } - return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0 + @Override + public int compareTo(FieldId other) { + if (declaringClassIndex != other.declaringClassIndex) { + return Unsigned.compare(declaringClassIndex, other.declaringClassIndex); } - - public void writeTo(DexBuffer.Section out) { - out.writeUnsignedShort(declaringClassIndex); - out.writeUnsignedShort(typeIndex); - out.writeInt(nameIndex); + if (nameIndex != other.nameIndex) { + return Unsigned.compare(nameIndex, other.nameIndex); } + return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0 + } + + public void writeTo(DexBuffer.Section out) { + out.writeUnsignedShort(declaringClassIndex); + out.writeUnsignedShort(typeIndex); + out.writeInt(nameIndex); + } - @Override public String toString() { - if (buffer == null) { - return declaringClassIndex + " " + typeIndex + " " + nameIndex; - } - return buffer.typeNames().get(typeIndex) + "." + buffer.strings().get(nameIndex); + @Override + public String toString() { + if (buffer == null) { + return declaringClassIndex + " " + typeIndex + " " + nameIndex; } + return buffer.typeNames().get(typeIndex) + "." + buffer.strings().get(nameIndex); + } } diff --git a/dx/src/com/android/jack/dx/io/IndexType.java b/dx/src/com/android/jack/dx/io/IndexType.java index 0538966..515af59 100644 --- a/dx/src/com/android/jack/dx/io/IndexType.java +++ b/dx/src/com/android/jack/dx/io/IndexType.java @@ -20,33 +20,33 @@ package com.android.jack.dx.io; * The various types that an index in a Dalvik instruction might refer to. */ public enum IndexType { - /** "Unknown." Used for undefined opcodes. */ - UNKNOWN, + /** "Unknown." Used for undefined opcodes. */ + UNKNOWN, - /** no index used */ - NONE, + /** no index used */ + NONE, - /** "It depends." Used for {@code throw-verification-error}. */ - VARIES, + /** "It depends." Used for {@code throw-verification-error}. */ + VARIES, - /** type reference index */ - TYPE_REF, + /** type reference index */ + TYPE_REF, - /** string reference index */ - STRING_REF, + /** string reference index */ + STRING_REF, - /** method reference index */ - METHOD_REF, + /** method reference index */ + METHOD_REF, - /** field reference index */ - FIELD_REF, + /** field reference index */ + FIELD_REF, - /** inline method index (for inline linked method invocations) */ - INLINE_METHOD, + /** inline method index (for inline linked method invocations) */ + INLINE_METHOD, - /** direct vtable offset (for static linked method invocations) */ - VTABLE_OFFSET, + /** direct vtable offset (for static linked method invocations) */ + VTABLE_OFFSET, - /** direct field offset (for static linked field accesses) */ - FIELD_OFFSET; + /** direct field offset (for static linked field accesses) */ + FIELD_OFFSET; } diff --git a/dx/src/com/android/jack/dx/io/MethodId.java b/dx/src/com/android/jack/dx/io/MethodId.java index 172c02f..da2a683 100644 --- a/dx/src/com/android/jack/dx/io/MethodId.java +++ b/dx/src/com/android/jack/dx/io/MethodId.java @@ -18,53 +18,57 @@ package com.android.jack.dx.io; import com.android.jack.dx.util.Unsigned; +/** + * TODO(jack team) + */ public final class MethodId implements Comparable<MethodId> { - private final DexBuffer buffer; - private final int declaringClassIndex; - private final int protoIndex; - private final int nameIndex; + private final DexBuffer buffer; + private final int declaringClassIndex; + private final int protoIndex; + private final int nameIndex; - public MethodId(DexBuffer buffer, int declaringClassIndex, int protoIndex, int nameIndex) { - this.buffer = buffer; - this.declaringClassIndex = declaringClassIndex; - this.protoIndex = protoIndex; - this.nameIndex = nameIndex; - } + public MethodId(DexBuffer buffer, int declaringClassIndex, int protoIndex, int nameIndex) { + this.buffer = buffer; + this.declaringClassIndex = declaringClassIndex; + this.protoIndex = protoIndex; + this.nameIndex = nameIndex; + } - public int getDeclaringClassIndex() { - return declaringClassIndex; - } + public int getDeclaringClassIndex() { + return declaringClassIndex; + } - public int getProtoIndex() { - return protoIndex; - } + public int getProtoIndex() { + return protoIndex; + } - public int getNameIndex() { - return nameIndex; - } + public int getNameIndex() { + return nameIndex; + } - public int compareTo(MethodId other) { - if (declaringClassIndex != other.declaringClassIndex) { - return Unsigned.compare(declaringClassIndex, other.declaringClassIndex); - } - if (nameIndex != other.nameIndex) { - return Unsigned.compare(nameIndex, other.nameIndex); - } - return Unsigned.compare(protoIndex, other.protoIndex); + @Override + public int compareTo(MethodId other) { + if (declaringClassIndex != other.declaringClassIndex) { + return Unsigned.compare(declaringClassIndex, other.declaringClassIndex); } - - public void writeTo(DexBuffer.Section out) { - out.writeUnsignedShort(declaringClassIndex); - out.writeUnsignedShort(protoIndex); - out.writeInt(nameIndex); + if (nameIndex != other.nameIndex) { + return Unsigned.compare(nameIndex, other.nameIndex); } + return Unsigned.compare(protoIndex, other.protoIndex); + } + + public void writeTo(DexBuffer.Section out) { + out.writeUnsignedShort(declaringClassIndex); + out.writeUnsignedShort(protoIndex); + out.writeInt(nameIndex); + } - @Override public String toString() { - if (buffer == null) { - return declaringClassIndex + " " + protoIndex + " " + nameIndex; - } - return buffer.typeNames().get(declaringClassIndex) - + "." + buffer.strings().get(nameIndex) - + buffer.readTypeList(buffer.protoIds().get(protoIndex).getParametersOffset()); + @Override + public String toString() { + if (buffer == null) { + return declaringClassIndex + " " + protoIndex + " " + nameIndex; } + return buffer.typeNames().get(declaringClassIndex) + "." + buffer.strings().get(nameIndex) + + buffer.readTypeList(buffer.protoIds().get(protoIndex).getParametersOffset()); + } } diff --git a/dx/src/com/android/jack/dx/io/OpcodeInfo.java b/dx/src/com/android/jack/dx/io/OpcodeInfo.java index 069784a..655e528 100644 --- a/dx/src/com/android/jack/dx/io/OpcodeInfo.java +++ b/dx/src/com/android/jack/dx/io/OpcodeInfo.java @@ -23,1243 +23,1016 @@ import com.android.jack.dx.util.Hex; * Information about each Dalvik opcode. */ public final class OpcodeInfo { - /* - * TODO: Merge at least most of the info from the Dops class into - * this one. - */ - - /** non-null; array containing all the information */ - private static final Info[] INFO; - - /** - * pseudo-opcode used for nonstandard formatted "instructions" - * (which are mostly not actually instructions, though they do - * appear in instruction lists). TODO: Retire the usage of this - * constant. - */ - public static final Info SPECIAL_FORMAT = - new Info(Opcodes.SPECIAL_FORMAT, "<special>", - InstructionCodec.FORMAT_00X, IndexType.NONE); - - // TODO: These payload opcodes should be generated by opcode-gen. - - public static final Info PACKED_SWITCH_PAYLOAD = - new Info(Opcodes.PACKED_SWITCH_PAYLOAD, "packed-switch-payload", - InstructionCodec.FORMAT_PACKED_SWITCH_PAYLOAD, - IndexType.NONE); - - public static final Info SPARSE_SWITCH_PAYLOAD = - new Info(Opcodes.SPARSE_SWITCH_PAYLOAD, "sparse-switch-payload", - InstructionCodec.FORMAT_SPARSE_SWITCH_PAYLOAD, - IndexType.NONE); - - public static final Info FILL_ARRAY_DATA_PAYLOAD = - new Info(Opcodes.FILL_ARRAY_DATA_PAYLOAD, "fill-array-data-payload", - InstructionCodec.FORMAT_FILL_ARRAY_DATA_PAYLOAD, - IndexType.NONE); - - // BEGIN(opcode-info-defs); GENERATED AUTOMATICALLY BY opcode-gen - public static final Info NOP = - new Info(Opcodes.NOP, "nop", - InstructionCodec.FORMAT_10X, IndexType.NONE); - - public static final Info MOVE = - new Info(Opcodes.MOVE, "move", - InstructionCodec.FORMAT_12X, IndexType.NONE); - - public static final Info MOVE_FROM16 = - new Info(Opcodes.MOVE_FROM16, "move/from16", - InstructionCodec.FORMAT_22X, IndexType.NONE); - - public static final Info MOVE_16 = - new Info(Opcodes.MOVE_16, "move/16", - InstructionCodec.FORMAT_32X, IndexType.NONE); - - public static final Info MOVE_WIDE = - new Info(Opcodes.MOVE_WIDE, "move-wide", - InstructionCodec.FORMAT_12X, IndexType.NONE); - - public static final Info MOVE_WIDE_FROM16 = - new Info(Opcodes.MOVE_WIDE_FROM16, "move-wide/from16", - InstructionCodec.FORMAT_22X, IndexType.NONE); - - public static final Info MOVE_WIDE_16 = - new Info(Opcodes.MOVE_WIDE_16, "move-wide/16", - InstructionCodec.FORMAT_32X, IndexType.NONE); - - public static final Info MOVE_OBJECT = - new Info(Opcodes.MOVE_OBJECT, "move-object", - InstructionCodec.FORMAT_12X, IndexType.NONE); - - public static final Info MOVE_OBJECT_FROM16 = - new Info(Opcodes.MOVE_OBJECT_FROM16, "move-object/from16", - InstructionCodec.FORMAT_22X, IndexType.NONE); - - public static final Info MOVE_OBJECT_16 = - new Info(Opcodes.MOVE_OBJECT_16, "move-object/16", - InstructionCodec.FORMAT_32X, IndexType.NONE); - - public static final Info MOVE_RESULT = - new Info(Opcodes.MOVE_RESULT, "move-result", - InstructionCodec.FORMAT_11X, IndexType.NONE); - - public static final Info MOVE_RESULT_WIDE = - new Info(Opcodes.MOVE_RESULT_WIDE, "move-result-wide", - InstructionCodec.FORMAT_11X, IndexType.NONE); - - public static final Info MOVE_RESULT_OBJECT = - new Info(Opcodes.MOVE_RESULT_OBJECT, "move-result-object", - InstructionCodec.FORMAT_11X, IndexType.NONE); - - public static final Info MOVE_EXCEPTION = - new Info(Opcodes.MOVE_EXCEPTION, "move-exception", - InstructionCodec.FORMAT_11X, IndexType.NONE); - - public static final Info RETURN_VOID = - new Info(Opcodes.RETURN_VOID, "return-void", - InstructionCodec.FORMAT_10X, IndexType.NONE); - - public static final Info RETURN = - new Info(Opcodes.RETURN, "return", - InstructionCodec.FORMAT_11X, IndexType.NONE); - - public static final Info RETURN_WIDE = - new Info(Opcodes.RETURN_WIDE, "return-wide", - InstructionCodec.FORMAT_11X, IndexType.NONE); - - public static final Info RETURN_OBJECT = - new Info(Opcodes.RETURN_OBJECT, "return-object", - InstructionCodec.FORMAT_11X, IndexType.NONE); - - public static final Info CONST_4 = - new Info(Opcodes.CONST_4, "const/4", - InstructionCodec.FORMAT_11N, IndexType.NONE); - - public static final Info CONST_16 = - new Info(Opcodes.CONST_16, "const/16", - InstructionCodec.FORMAT_21S, IndexType.NONE); + /* + * TODO(dx team): Merge at least most of the info from the Dops class into + * this one. + */ - public static final Info CONST = - new Info(Opcodes.CONST, "const", - InstructionCodec.FORMAT_31I, IndexType.NONE); + /** non-null; array containing all the information */ + private static final Info[] INFO; - public static final Info CONST_HIGH16 = - new Info(Opcodes.CONST_HIGH16, "const/high16", - InstructionCodec.FORMAT_21H, IndexType.NONE); + /** + * pseudo-opcode used for nonstandard formatted "instructions" + * (which are mostly not actually instructions, though they do + * appear in instruction lists). TODO(dx team): Retire the usage of this + * constant. + */ + public static final Info SPECIAL_FORMAT = + new Info(Opcodes.SPECIAL_FORMAT, "<special>", InstructionCodec.FORMAT_00X, IndexType.NONE); - public static final Info CONST_WIDE_16 = - new Info(Opcodes.CONST_WIDE_16, "const-wide/16", - InstructionCodec.FORMAT_21S, IndexType.NONE); + // TODO(dx team): These payload opcodes should be generated by opcode-gen. - public static final Info CONST_WIDE_32 = - new Info(Opcodes.CONST_WIDE_32, "const-wide/32", - InstructionCodec.FORMAT_31I, IndexType.NONE); + public static final Info PACKED_SWITCH_PAYLOAD = new Info(Opcodes.PACKED_SWITCH_PAYLOAD, + "packed-switch-payload", InstructionCodec.FORMAT_PACKED_SWITCH_PAYLOAD, IndexType.NONE); - public static final Info CONST_WIDE = - new Info(Opcodes.CONST_WIDE, "const-wide", - InstructionCodec.FORMAT_51L, IndexType.NONE); + public static final Info SPARSE_SWITCH_PAYLOAD = new Info(Opcodes.SPARSE_SWITCH_PAYLOAD, + "sparse-switch-payload", InstructionCodec.FORMAT_SPARSE_SWITCH_PAYLOAD, IndexType.NONE); - public static final Info CONST_WIDE_HIGH16 = - new Info(Opcodes.CONST_WIDE_HIGH16, "const-wide/high16", - InstructionCodec.FORMAT_21H, IndexType.NONE); + public static final Info FILL_ARRAY_DATA_PAYLOAD = new Info(Opcodes.FILL_ARRAY_DATA_PAYLOAD, + "fill-array-data-payload", InstructionCodec.FORMAT_FILL_ARRAY_DATA_PAYLOAD, IndexType.NONE); - public static final Info CONST_STRING = - new Info(Opcodes.CONST_STRING, "const-string", - InstructionCodec.FORMAT_21C, IndexType.STRING_REF); + // BEGIN(opcode-info-defs); GENERATED AUTOMATICALLY BY opcode-gen + public static final Info NOP = + new Info(Opcodes.NOP, "nop", InstructionCodec.FORMAT_10X, IndexType.NONE); - public static final Info CONST_STRING_JUMBO = - new Info(Opcodes.CONST_STRING_JUMBO, "const-string/jumbo", - InstructionCodec.FORMAT_31C, IndexType.STRING_REF); + public static final Info MOVE = + new Info(Opcodes.MOVE, "move", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info CONST_CLASS = - new Info(Opcodes.CONST_CLASS, "const-class", - InstructionCodec.FORMAT_21C, IndexType.TYPE_REF); + public static final Info MOVE_FROM16 = + new Info(Opcodes.MOVE_FROM16, "move/from16", InstructionCodec.FORMAT_22X, IndexType.NONE); - public static final Info MONITOR_ENTER = - new Info(Opcodes.MONITOR_ENTER, "monitor-enter", - InstructionCodec.FORMAT_11X, IndexType.NONE); + public static final Info MOVE_16 = + new Info(Opcodes.MOVE_16, "move/16", InstructionCodec.FORMAT_32X, IndexType.NONE); - public static final Info MONITOR_EXIT = - new Info(Opcodes.MONITOR_EXIT, "monitor-exit", - InstructionCodec.FORMAT_11X, IndexType.NONE); + public static final Info MOVE_WIDE = + new Info(Opcodes.MOVE_WIDE, "move-wide", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info CHECK_CAST = - new Info(Opcodes.CHECK_CAST, "check-cast", - InstructionCodec.FORMAT_21C, IndexType.TYPE_REF); + public static final Info MOVE_WIDE_FROM16 = new Info(Opcodes.MOVE_WIDE_FROM16, "move-wide/from16", + InstructionCodec.FORMAT_22X, IndexType.NONE); - public static final Info INSTANCE_OF = - new Info(Opcodes.INSTANCE_OF, "instance-of", - InstructionCodec.FORMAT_22C, IndexType.TYPE_REF); + public static final Info MOVE_WIDE_16 = + new Info(Opcodes.MOVE_WIDE_16, "move-wide/16", InstructionCodec.FORMAT_32X, IndexType.NONE); - public static final Info ARRAY_LENGTH = - new Info(Opcodes.ARRAY_LENGTH, "array-length", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info MOVE_OBJECT = + new Info(Opcodes.MOVE_OBJECT, "move-object", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info NEW_INSTANCE = - new Info(Opcodes.NEW_INSTANCE, "new-instance", - InstructionCodec.FORMAT_21C, IndexType.TYPE_REF); + public static final Info MOVE_OBJECT_FROM16 = new Info(Opcodes.MOVE_OBJECT_FROM16, + "move-object/from16", InstructionCodec.FORMAT_22X, IndexType.NONE); - public static final Info NEW_ARRAY = - new Info(Opcodes.NEW_ARRAY, "new-array", - InstructionCodec.FORMAT_22C, IndexType.TYPE_REF); + public static final Info MOVE_OBJECT_16 = new Info(Opcodes.MOVE_OBJECT_16, "move-object/16", + InstructionCodec.FORMAT_32X, IndexType.NONE); - public static final Info FILLED_NEW_ARRAY = - new Info(Opcodes.FILLED_NEW_ARRAY, "filled-new-array", - InstructionCodec.FORMAT_35C, IndexType.TYPE_REF); + public static final Info MOVE_RESULT = + new Info(Opcodes.MOVE_RESULT, "move-result", InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info FILLED_NEW_ARRAY_RANGE = - new Info(Opcodes.FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", - InstructionCodec.FORMAT_3RC, IndexType.TYPE_REF); + public static final Info MOVE_RESULT_WIDE = new Info(Opcodes.MOVE_RESULT_WIDE, "move-result-wide", + InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info FILL_ARRAY_DATA = - new Info(Opcodes.FILL_ARRAY_DATA, "fill-array-data", - InstructionCodec.FORMAT_31T, IndexType.NONE); + public static final Info MOVE_RESULT_OBJECT = new Info(Opcodes.MOVE_RESULT_OBJECT, + "move-result-object", InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info THROW = - new Info(Opcodes.THROW, "throw", - InstructionCodec.FORMAT_11X, IndexType.NONE); + public static final Info MOVE_EXCEPTION = new Info(Opcodes.MOVE_EXCEPTION, "move-exception", + InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info GOTO = - new Info(Opcodes.GOTO, "goto", - InstructionCodec.FORMAT_10T, IndexType.NONE); + public static final Info RETURN_VOID = + new Info(Opcodes.RETURN_VOID, "return-void", InstructionCodec.FORMAT_10X, IndexType.NONE); - public static final Info GOTO_16 = - new Info(Opcodes.GOTO_16, "goto/16", - InstructionCodec.FORMAT_20T, IndexType.NONE); + public static final Info RETURN = + new Info(Opcodes.RETURN, "return", InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info GOTO_32 = - new Info(Opcodes.GOTO_32, "goto/32", - InstructionCodec.FORMAT_30T, IndexType.NONE); + public static final Info RETURN_WIDE = + new Info(Opcodes.RETURN_WIDE, "return-wide", InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info PACKED_SWITCH = - new Info(Opcodes.PACKED_SWITCH, "packed-switch", - InstructionCodec.FORMAT_31T, IndexType.NONE); + public static final Info RETURN_OBJECT = + new Info(Opcodes.RETURN_OBJECT, "return-object", InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info SPARSE_SWITCH = - new Info(Opcodes.SPARSE_SWITCH, "sparse-switch", - InstructionCodec.FORMAT_31T, IndexType.NONE); + public static final Info CONST_4 = + new Info(Opcodes.CONST_4, "const/4", InstructionCodec.FORMAT_11N, IndexType.NONE); - public static final Info CMPL_FLOAT = - new Info(Opcodes.CMPL_FLOAT, "cmpl-float", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CONST_16 = + new Info(Opcodes.CONST_16, "const/16", InstructionCodec.FORMAT_21S, IndexType.NONE); - public static final Info CMPG_FLOAT = - new Info(Opcodes.CMPG_FLOAT, "cmpg-float", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CONST = + new Info(Opcodes.CONST, "const", InstructionCodec.FORMAT_31I, IndexType.NONE); - public static final Info CMPL_DOUBLE = - new Info(Opcodes.CMPL_DOUBLE, "cmpl-double", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CONST_HIGH16 = + new Info(Opcodes.CONST_HIGH16, "const/high16", InstructionCodec.FORMAT_21H, IndexType.NONE); - public static final Info CMPG_DOUBLE = - new Info(Opcodes.CMPG_DOUBLE, "cmpg-double", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CONST_WIDE_16 = + new Info(Opcodes.CONST_WIDE_16, "const-wide/16", InstructionCodec.FORMAT_21S, IndexType.NONE); - public static final Info CMP_LONG = - new Info(Opcodes.CMP_LONG, "cmp-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CONST_WIDE_32 = + new Info(Opcodes.CONST_WIDE_32, "const-wide/32", InstructionCodec.FORMAT_31I, IndexType.NONE); - public static final Info IF_EQ = - new Info(Opcodes.IF_EQ, "if-eq", - InstructionCodec.FORMAT_22T, IndexType.NONE); + public static final Info CONST_WIDE = + new Info(Opcodes.CONST_WIDE, "const-wide", InstructionCodec.FORMAT_51L, IndexType.NONE); - public static final Info IF_NE = - new Info(Opcodes.IF_NE, "if-ne", - InstructionCodec.FORMAT_22T, IndexType.NONE); + public static final Info CONST_WIDE_HIGH16 = new Info(Opcodes.CONST_WIDE_HIGH16, + "const-wide/high16", InstructionCodec.FORMAT_21H, IndexType.NONE); - public static final Info IF_LT = - new Info(Opcodes.IF_LT, "if-lt", - InstructionCodec.FORMAT_22T, IndexType.NONE); + public static final Info CONST_STRING = new Info(Opcodes.CONST_STRING, "const-string", + InstructionCodec.FORMAT_21C, IndexType.STRING_REF); - public static final Info IF_GE = - new Info(Opcodes.IF_GE, "if-ge", - InstructionCodec.FORMAT_22T, IndexType.NONE); + public static final Info CONST_STRING_JUMBO = new Info(Opcodes.CONST_STRING_JUMBO, + "const-string/jumbo", InstructionCodec.FORMAT_31C, IndexType.STRING_REF); - public static final Info IF_GT = - new Info(Opcodes.IF_GT, "if-gt", - InstructionCodec.FORMAT_22T, IndexType.NONE); + public static final Info CONST_CLASS = + new Info(Opcodes.CONST_CLASS, "const-class", InstructionCodec.FORMAT_21C, IndexType.TYPE_REF); - public static final Info IF_LE = - new Info(Opcodes.IF_LE, "if-le", - InstructionCodec.FORMAT_22T, IndexType.NONE); + public static final Info MONITOR_ENTER = + new Info(Opcodes.MONITOR_ENTER, "monitor-enter", InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info IF_EQZ = - new Info(Opcodes.IF_EQZ, "if-eqz", - InstructionCodec.FORMAT_21T, IndexType.NONE); + public static final Info MONITOR_EXIT = + new Info(Opcodes.MONITOR_EXIT, "monitor-exit", InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info IF_NEZ = - new Info(Opcodes.IF_NEZ, "if-nez", - InstructionCodec.FORMAT_21T, IndexType.NONE); + public static final Info CHECK_CAST = + new Info(Opcodes.CHECK_CAST, "check-cast", InstructionCodec.FORMAT_21C, IndexType.TYPE_REF); - public static final Info IF_LTZ = - new Info(Opcodes.IF_LTZ, "if-ltz", - InstructionCodec.FORMAT_21T, IndexType.NONE); + public static final Info INSTANCE_OF = + new Info(Opcodes.INSTANCE_OF, "instance-of", InstructionCodec.FORMAT_22C, IndexType.TYPE_REF); - public static final Info IF_GEZ = - new Info(Opcodes.IF_GEZ, "if-gez", - InstructionCodec.FORMAT_21T, IndexType.NONE); + public static final Info ARRAY_LENGTH = + new Info(Opcodes.ARRAY_LENGTH, "array-length", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info IF_GTZ = - new Info(Opcodes.IF_GTZ, "if-gtz", - InstructionCodec.FORMAT_21T, IndexType.NONE); + public static final Info NEW_INSTANCE = new Info(Opcodes.NEW_INSTANCE, "new-instance", + InstructionCodec.FORMAT_21C, IndexType.TYPE_REF); - public static final Info IF_LEZ = - new Info(Opcodes.IF_LEZ, "if-lez", - InstructionCodec.FORMAT_21T, IndexType.NONE); + public static final Info NEW_ARRAY = + new Info(Opcodes.NEW_ARRAY, "new-array", InstructionCodec.FORMAT_22C, IndexType.TYPE_REF); - public static final Info AGET = - new Info(Opcodes.AGET, "aget", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info FILLED_NEW_ARRAY = new Info(Opcodes.FILLED_NEW_ARRAY, "filled-new-array", + InstructionCodec.FORMAT_35C, IndexType.TYPE_REF); - public static final Info AGET_WIDE = - new Info(Opcodes.AGET_WIDE, "aget-wide", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info FILLED_NEW_ARRAY_RANGE = new Info(Opcodes.FILLED_NEW_ARRAY_RANGE, + "filled-new-array/range", InstructionCodec.FORMAT_3RC, IndexType.TYPE_REF); - public static final Info AGET_OBJECT = - new Info(Opcodes.AGET_OBJECT, "aget-object", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info FILL_ARRAY_DATA = new Info(Opcodes.FILL_ARRAY_DATA, "fill-array-data", + InstructionCodec.FORMAT_31T, IndexType.NONE); - public static final Info AGET_BOOLEAN = - new Info(Opcodes.AGET_BOOLEAN, "aget-boolean", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info THROW = + new Info(Opcodes.THROW, "throw", InstructionCodec.FORMAT_11X, IndexType.NONE); - public static final Info AGET_BYTE = - new Info(Opcodes.AGET_BYTE, "aget-byte", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info GOTO = + new Info(Opcodes.GOTO, "goto", InstructionCodec.FORMAT_10T, IndexType.NONE); - public static final Info AGET_CHAR = - new Info(Opcodes.AGET_CHAR, "aget-char", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info GOTO_16 = + new Info(Opcodes.GOTO_16, "goto/16", InstructionCodec.FORMAT_20T, IndexType.NONE); - public static final Info AGET_SHORT = - new Info(Opcodes.AGET_SHORT, "aget-short", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info GOTO_32 = + new Info(Opcodes.GOTO_32, "goto/32", InstructionCodec.FORMAT_30T, IndexType.NONE); - public static final Info APUT = - new Info(Opcodes.APUT, "aput", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info PACKED_SWITCH = + new Info(Opcodes.PACKED_SWITCH, "packed-switch", InstructionCodec.FORMAT_31T, IndexType.NONE); - public static final Info APUT_WIDE = - new Info(Opcodes.APUT_WIDE, "aput-wide", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info SPARSE_SWITCH = + new Info(Opcodes.SPARSE_SWITCH, "sparse-switch", InstructionCodec.FORMAT_31T, IndexType.NONE); - public static final Info APUT_OBJECT = - new Info(Opcodes.APUT_OBJECT, "aput-object", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CMPL_FLOAT = + new Info(Opcodes.CMPL_FLOAT, "cmpl-float", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info APUT_BOOLEAN = - new Info(Opcodes.APUT_BOOLEAN, "aput-boolean", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CMPG_FLOAT = + new Info(Opcodes.CMPG_FLOAT, "cmpg-float", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info APUT_BYTE = - new Info(Opcodes.APUT_BYTE, "aput-byte", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CMPL_DOUBLE = + new Info(Opcodes.CMPL_DOUBLE, "cmpl-double", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info APUT_CHAR = - new Info(Opcodes.APUT_CHAR, "aput-char", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CMPG_DOUBLE = + new Info(Opcodes.CMPG_DOUBLE, "cmpg-double", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info APUT_SHORT = - new Info(Opcodes.APUT_SHORT, "aput-short", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info CMP_LONG = + new Info(Opcodes.CMP_LONG, "cmp-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info IGET = - new Info(Opcodes.IGET, "iget", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_EQ = + new Info(Opcodes.IF_EQ, "if-eq", InstructionCodec.FORMAT_22T, IndexType.NONE); - public static final Info IGET_WIDE = - new Info(Opcodes.IGET_WIDE, "iget-wide", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_NE = + new Info(Opcodes.IF_NE, "if-ne", InstructionCodec.FORMAT_22T, IndexType.NONE); - public static final Info IGET_OBJECT = - new Info(Opcodes.IGET_OBJECT, "iget-object", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_LT = + new Info(Opcodes.IF_LT, "if-lt", InstructionCodec.FORMAT_22T, IndexType.NONE); - public static final Info IGET_BOOLEAN = - new Info(Opcodes.IGET_BOOLEAN, "iget-boolean", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_GE = + new Info(Opcodes.IF_GE, "if-ge", InstructionCodec.FORMAT_22T, IndexType.NONE); - public static final Info IGET_BYTE = - new Info(Opcodes.IGET_BYTE, "iget-byte", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_GT = + new Info(Opcodes.IF_GT, "if-gt", InstructionCodec.FORMAT_22T, IndexType.NONE); - public static final Info IGET_CHAR = - new Info(Opcodes.IGET_CHAR, "iget-char", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_LE = + new Info(Opcodes.IF_LE, "if-le", InstructionCodec.FORMAT_22T, IndexType.NONE); - public static final Info IGET_SHORT = - new Info(Opcodes.IGET_SHORT, "iget-short", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_EQZ = + new Info(Opcodes.IF_EQZ, "if-eqz", InstructionCodec.FORMAT_21T, IndexType.NONE); - public static final Info IPUT = - new Info(Opcodes.IPUT, "iput", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_NEZ = + new Info(Opcodes.IF_NEZ, "if-nez", InstructionCodec.FORMAT_21T, IndexType.NONE); - public static final Info IPUT_WIDE = - new Info(Opcodes.IPUT_WIDE, "iput-wide", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_LTZ = + new Info(Opcodes.IF_LTZ, "if-ltz", InstructionCodec.FORMAT_21T, IndexType.NONE); - public static final Info IPUT_OBJECT = - new Info(Opcodes.IPUT_OBJECT, "iput-object", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_GEZ = + new Info(Opcodes.IF_GEZ, "if-gez", InstructionCodec.FORMAT_21T, IndexType.NONE); - public static final Info IPUT_BOOLEAN = - new Info(Opcodes.IPUT_BOOLEAN, "iput-boolean", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_GTZ = + new Info(Opcodes.IF_GTZ, "if-gtz", InstructionCodec.FORMAT_21T, IndexType.NONE); - public static final Info IPUT_BYTE = - new Info(Opcodes.IPUT_BYTE, "iput-byte", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info IF_LEZ = + new Info(Opcodes.IF_LEZ, "if-lez", InstructionCodec.FORMAT_21T, IndexType.NONE); - public static final Info IPUT_CHAR = - new Info(Opcodes.IPUT_CHAR, "iput-char", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info AGET = + new Info(Opcodes.AGET, "aget", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info IPUT_SHORT = - new Info(Opcodes.IPUT_SHORT, "iput-short", - InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); + public static final Info AGET_WIDE = + new Info(Opcodes.AGET_WIDE, "aget-wide", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SGET = - new Info(Opcodes.SGET, "sget", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info AGET_OBJECT = + new Info(Opcodes.AGET_OBJECT, "aget-object", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SGET_WIDE = - new Info(Opcodes.SGET_WIDE, "sget-wide", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info AGET_BOOLEAN = + new Info(Opcodes.AGET_BOOLEAN, "aget-boolean", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SGET_OBJECT = - new Info(Opcodes.SGET_OBJECT, "sget-object", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info AGET_BYTE = + new Info(Opcodes.AGET_BYTE, "aget-byte", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SGET_BOOLEAN = - new Info(Opcodes.SGET_BOOLEAN, "sget-boolean", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info AGET_CHAR = + new Info(Opcodes.AGET_CHAR, "aget-char", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SGET_BYTE = - new Info(Opcodes.SGET_BYTE, "sget-byte", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info AGET_SHORT = + new Info(Opcodes.AGET_SHORT, "aget-short", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SGET_CHAR = - new Info(Opcodes.SGET_CHAR, "sget-char", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info APUT = + new Info(Opcodes.APUT, "aput", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SGET_SHORT = - new Info(Opcodes.SGET_SHORT, "sget-short", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info APUT_WIDE = + new Info(Opcodes.APUT_WIDE, "aput-wide", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SPUT = - new Info(Opcodes.SPUT, "sput", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info APUT_OBJECT = + new Info(Opcodes.APUT_OBJECT, "aput-object", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SPUT_WIDE = - new Info(Opcodes.SPUT_WIDE, "sput-wide", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info APUT_BOOLEAN = + new Info(Opcodes.APUT_BOOLEAN, "aput-boolean", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SPUT_OBJECT = - new Info(Opcodes.SPUT_OBJECT, "sput-object", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info APUT_BYTE = + new Info(Opcodes.APUT_BYTE, "aput-byte", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SPUT_BOOLEAN = - new Info(Opcodes.SPUT_BOOLEAN, "sput-boolean", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info APUT_CHAR = + new Info(Opcodes.APUT_CHAR, "aput-char", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SPUT_BYTE = - new Info(Opcodes.SPUT_BYTE, "sput-byte", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info APUT_SHORT = + new Info(Opcodes.APUT_SHORT, "aput-short", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SPUT_CHAR = - new Info(Opcodes.SPUT_CHAR, "sput-char", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info IGET = + new Info(Opcodes.IGET, "iget", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info SPUT_SHORT = - new Info(Opcodes.SPUT_SHORT, "sput-short", - InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); + public static final Info IGET_WIDE = + new Info(Opcodes.IGET_WIDE, "iget-wide", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_VIRTUAL = - new Info(Opcodes.INVOKE_VIRTUAL, "invoke-virtual", - InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); + public static final Info IGET_OBJECT = new Info(Opcodes.IGET_OBJECT, "iget-object", + InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_SUPER = - new Info(Opcodes.INVOKE_SUPER, "invoke-super", - InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); + public static final Info IGET_BOOLEAN = new Info(Opcodes.IGET_BOOLEAN, "iget-boolean", + InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_DIRECT = - new Info(Opcodes.INVOKE_DIRECT, "invoke-direct", - InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); + public static final Info IGET_BYTE = + new Info(Opcodes.IGET_BYTE, "iget-byte", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_STATIC = - new Info(Opcodes.INVOKE_STATIC, "invoke-static", - InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); + public static final Info IGET_CHAR = + new Info(Opcodes.IGET_CHAR, "iget-char", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_INTERFACE = - new Info(Opcodes.INVOKE_INTERFACE, "invoke-interface", - InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); + public static final Info IGET_SHORT = + new Info(Opcodes.IGET_SHORT, "iget-short", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_VIRTUAL_RANGE = - new Info(Opcodes.INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", - InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); + public static final Info IPUT = + new Info(Opcodes.IPUT, "iput", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_SUPER_RANGE = - new Info(Opcodes.INVOKE_SUPER_RANGE, "invoke-super/range", - InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); + public static final Info IPUT_WIDE = + new Info(Opcodes.IPUT_WIDE, "iput-wide", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_DIRECT_RANGE = - new Info(Opcodes.INVOKE_DIRECT_RANGE, "invoke-direct/range", - InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); + public static final Info IPUT_OBJECT = new Info(Opcodes.IPUT_OBJECT, "iput-object", + InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_STATIC_RANGE = - new Info(Opcodes.INVOKE_STATIC_RANGE, "invoke-static/range", - InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); + public static final Info IPUT_BOOLEAN = new Info(Opcodes.IPUT_BOOLEAN, "iput-boolean", + InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info INVOKE_INTERFACE_RANGE = - new Info(Opcodes.INVOKE_INTERFACE_RANGE, "invoke-interface/range", - InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); + public static final Info IPUT_BYTE = + new Info(Opcodes.IPUT_BYTE, "iput-byte", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info NEG_INT = - new Info(Opcodes.NEG_INT, "neg-int", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info IPUT_CHAR = + new Info(Opcodes.IPUT_CHAR, "iput-char", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info NOT_INT = - new Info(Opcodes.NOT_INT, "not-int", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info IPUT_SHORT = + new Info(Opcodes.IPUT_SHORT, "iput-short", InstructionCodec.FORMAT_22C, IndexType.FIELD_REF); - public static final Info NEG_LONG = - new Info(Opcodes.NEG_LONG, "neg-long", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SGET = + new Info(Opcodes.SGET, "sget", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info NOT_LONG = - new Info(Opcodes.NOT_LONG, "not-long", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SGET_WIDE = + new Info(Opcodes.SGET_WIDE, "sget-wide", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info NEG_FLOAT = - new Info(Opcodes.NEG_FLOAT, "neg-float", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SGET_OBJECT = new Info(Opcodes.SGET_OBJECT, "sget-object", + InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info NEG_DOUBLE = - new Info(Opcodes.NEG_DOUBLE, "neg-double", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SGET_BOOLEAN = new Info(Opcodes.SGET_BOOLEAN, "sget-boolean", + InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info INT_TO_LONG = - new Info(Opcodes.INT_TO_LONG, "int-to-long", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SGET_BYTE = + new Info(Opcodes.SGET_BYTE, "sget-byte", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info INT_TO_FLOAT = - new Info(Opcodes.INT_TO_FLOAT, "int-to-float", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SGET_CHAR = + new Info(Opcodes.SGET_CHAR, "sget-char", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info INT_TO_DOUBLE = - new Info(Opcodes.INT_TO_DOUBLE, "int-to-double", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SGET_SHORT = + new Info(Opcodes.SGET_SHORT, "sget-short", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info LONG_TO_INT = - new Info(Opcodes.LONG_TO_INT, "long-to-int", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SPUT = + new Info(Opcodes.SPUT, "sput", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info LONG_TO_FLOAT = - new Info(Opcodes.LONG_TO_FLOAT, "long-to-float", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SPUT_WIDE = + new Info(Opcodes.SPUT_WIDE, "sput-wide", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info LONG_TO_DOUBLE = - new Info(Opcodes.LONG_TO_DOUBLE, "long-to-double", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SPUT_OBJECT = new Info(Opcodes.SPUT_OBJECT, "sput-object", + InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info FLOAT_TO_INT = - new Info(Opcodes.FLOAT_TO_INT, "float-to-int", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SPUT_BOOLEAN = new Info(Opcodes.SPUT_BOOLEAN, "sput-boolean", + InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info FLOAT_TO_LONG = - new Info(Opcodes.FLOAT_TO_LONG, "float-to-long", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SPUT_BYTE = + new Info(Opcodes.SPUT_BYTE, "sput-byte", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info FLOAT_TO_DOUBLE = - new Info(Opcodes.FLOAT_TO_DOUBLE, "float-to-double", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SPUT_CHAR = + new Info(Opcodes.SPUT_CHAR, "sput-char", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info DOUBLE_TO_INT = - new Info(Opcodes.DOUBLE_TO_INT, "double-to-int", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SPUT_SHORT = + new Info(Opcodes.SPUT_SHORT, "sput-short", InstructionCodec.FORMAT_21C, IndexType.FIELD_REF); - public static final Info DOUBLE_TO_LONG = - new Info(Opcodes.DOUBLE_TO_LONG, "double-to-long", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info INVOKE_VIRTUAL = new Info(Opcodes.INVOKE_VIRTUAL, "invoke-virtual", + InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); - public static final Info DOUBLE_TO_FLOAT = - new Info(Opcodes.DOUBLE_TO_FLOAT, "double-to-float", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info INVOKE_SUPER = new Info(Opcodes.INVOKE_SUPER, "invoke-super", + InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); - public static final Info INT_TO_BYTE = - new Info(Opcodes.INT_TO_BYTE, "int-to-byte", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info INVOKE_DIRECT = new Info(Opcodes.INVOKE_DIRECT, "invoke-direct", + InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); - public static final Info INT_TO_CHAR = - new Info(Opcodes.INT_TO_CHAR, "int-to-char", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info INVOKE_STATIC = new Info(Opcodes.INVOKE_STATIC, "invoke-static", + InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); - public static final Info INT_TO_SHORT = - new Info(Opcodes.INT_TO_SHORT, "int-to-short", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info INVOKE_INTERFACE = new Info(Opcodes.INVOKE_INTERFACE, "invoke-interface", + InstructionCodec.FORMAT_35C, IndexType.METHOD_REF); - public static final Info ADD_INT = - new Info(Opcodes.ADD_INT, "add-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INVOKE_VIRTUAL_RANGE = new Info(Opcodes.INVOKE_VIRTUAL_RANGE, + "invoke-virtual/range", InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); - public static final Info SUB_INT = - new Info(Opcodes.SUB_INT, "sub-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INVOKE_SUPER_RANGE = new Info(Opcodes.INVOKE_SUPER_RANGE, + "invoke-super/range", InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); - public static final Info MUL_INT = - new Info(Opcodes.MUL_INT, "mul-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INVOKE_DIRECT_RANGE = new Info(Opcodes.INVOKE_DIRECT_RANGE, + "invoke-direct/range", InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); - public static final Info DIV_INT = - new Info(Opcodes.DIV_INT, "div-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INVOKE_STATIC_RANGE = new Info(Opcodes.INVOKE_STATIC_RANGE, + "invoke-static/range", InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); - public static final Info REM_INT = - new Info(Opcodes.REM_INT, "rem-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INVOKE_INTERFACE_RANGE = new Info(Opcodes.INVOKE_INTERFACE_RANGE, + "invoke-interface/range", InstructionCodec.FORMAT_3RC, IndexType.METHOD_REF); - public static final Info AND_INT = - new Info(Opcodes.AND_INT, "and-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info NEG_INT = + new Info(Opcodes.NEG_INT, "neg-int", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info OR_INT = - new Info(Opcodes.OR_INT, "or-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info NOT_INT = + new Info(Opcodes.NOT_INT, "not-int", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info XOR_INT = - new Info(Opcodes.XOR_INT, "xor-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info NEG_LONG = + new Info(Opcodes.NEG_LONG, "neg-long", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info SHL_INT = - new Info(Opcodes.SHL_INT, "shl-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info NOT_LONG = + new Info(Opcodes.NOT_LONG, "not-long", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info SHR_INT = - new Info(Opcodes.SHR_INT, "shr-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info NEG_FLOAT = + new Info(Opcodes.NEG_FLOAT, "neg-float", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info USHR_INT = - new Info(Opcodes.USHR_INT, "ushr-int", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info NEG_DOUBLE = + new Info(Opcodes.NEG_DOUBLE, "neg-double", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info ADD_LONG = - new Info(Opcodes.ADD_LONG, "add-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INT_TO_LONG = + new Info(Opcodes.INT_TO_LONG, "int-to-long", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info SUB_LONG = - new Info(Opcodes.SUB_LONG, "sub-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INT_TO_FLOAT = + new Info(Opcodes.INT_TO_FLOAT, "int-to-float", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info MUL_LONG = - new Info(Opcodes.MUL_LONG, "mul-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INT_TO_DOUBLE = + new Info(Opcodes.INT_TO_DOUBLE, "int-to-double", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info DIV_LONG = - new Info(Opcodes.DIV_LONG, "div-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info LONG_TO_INT = + new Info(Opcodes.LONG_TO_INT, "long-to-int", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info REM_LONG = - new Info(Opcodes.REM_LONG, "rem-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info LONG_TO_FLOAT = + new Info(Opcodes.LONG_TO_FLOAT, "long-to-float", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info AND_LONG = - new Info(Opcodes.AND_LONG, "and-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info LONG_TO_DOUBLE = new Info(Opcodes.LONG_TO_DOUBLE, "long-to-double", + InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info OR_LONG = - new Info(Opcodes.OR_LONG, "or-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info FLOAT_TO_INT = + new Info(Opcodes.FLOAT_TO_INT, "float-to-int", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info XOR_LONG = - new Info(Opcodes.XOR_LONG, "xor-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info FLOAT_TO_LONG = + new Info(Opcodes.FLOAT_TO_LONG, "float-to-long", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info SHL_LONG = - new Info(Opcodes.SHL_LONG, "shl-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info FLOAT_TO_DOUBLE = new Info(Opcodes.FLOAT_TO_DOUBLE, "float-to-double", + InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info SHR_LONG = - new Info(Opcodes.SHR_LONG, "shr-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info DOUBLE_TO_INT = + new Info(Opcodes.DOUBLE_TO_INT, "double-to-int", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info USHR_LONG = - new Info(Opcodes.USHR_LONG, "ushr-long", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info DOUBLE_TO_LONG = new Info(Opcodes.DOUBLE_TO_LONG, "double-to-long", + InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info ADD_FLOAT = - new Info(Opcodes.ADD_FLOAT, "add-float", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info DOUBLE_TO_FLOAT = new Info(Opcodes.DOUBLE_TO_FLOAT, "double-to-float", + InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info SUB_FLOAT = - new Info(Opcodes.SUB_FLOAT, "sub-float", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INT_TO_BYTE = + new Info(Opcodes.INT_TO_BYTE, "int-to-byte", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info MUL_FLOAT = - new Info(Opcodes.MUL_FLOAT, "mul-float", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INT_TO_CHAR = + new Info(Opcodes.INT_TO_CHAR, "int-to-char", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info DIV_FLOAT = - new Info(Opcodes.DIV_FLOAT, "div-float", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info INT_TO_SHORT = + new Info(Opcodes.INT_TO_SHORT, "int-to-short", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info REM_FLOAT = - new Info(Opcodes.REM_FLOAT, "rem-float", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info ADD_INT = + new Info(Opcodes.ADD_INT, "add-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info ADD_DOUBLE = - new Info(Opcodes.ADD_DOUBLE, "add-double", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info SUB_INT = + new Info(Opcodes.SUB_INT, "sub-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SUB_DOUBLE = - new Info(Opcodes.SUB_DOUBLE, "sub-double", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info MUL_INT = + new Info(Opcodes.MUL_INT, "mul-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info MUL_DOUBLE = - new Info(Opcodes.MUL_DOUBLE, "mul-double", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info DIV_INT = + new Info(Opcodes.DIV_INT, "div-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info DIV_DOUBLE = - new Info(Opcodes.DIV_DOUBLE, "div-double", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info REM_INT = + new Info(Opcodes.REM_INT, "rem-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info REM_DOUBLE = - new Info(Opcodes.REM_DOUBLE, "rem-double", - InstructionCodec.FORMAT_23X, IndexType.NONE); + public static final Info AND_INT = + new Info(Opcodes.AND_INT, "and-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info ADD_INT_2ADDR = - new Info(Opcodes.ADD_INT_2ADDR, "add-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info OR_INT = + new Info(Opcodes.OR_INT, "or-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SUB_INT_2ADDR = - new Info(Opcodes.SUB_INT_2ADDR, "sub-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info XOR_INT = + new Info(Opcodes.XOR_INT, "xor-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info MUL_INT_2ADDR = - new Info(Opcodes.MUL_INT_2ADDR, "mul-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SHL_INT = + new Info(Opcodes.SHL_INT, "shl-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info DIV_INT_2ADDR = - new Info(Opcodes.DIV_INT_2ADDR, "div-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SHR_INT = + new Info(Opcodes.SHR_INT, "shr-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info REM_INT_2ADDR = - new Info(Opcodes.REM_INT_2ADDR, "rem-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info USHR_INT = + new Info(Opcodes.USHR_INT, "ushr-int", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info AND_INT_2ADDR = - new Info(Opcodes.AND_INT_2ADDR, "and-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info ADD_LONG = + new Info(Opcodes.ADD_LONG, "add-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info OR_INT_2ADDR = - new Info(Opcodes.OR_INT_2ADDR, "or-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SUB_LONG = + new Info(Opcodes.SUB_LONG, "sub-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info XOR_INT_2ADDR = - new Info(Opcodes.XOR_INT_2ADDR, "xor-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info MUL_LONG = + new Info(Opcodes.MUL_LONG, "mul-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SHL_INT_2ADDR = - new Info(Opcodes.SHL_INT_2ADDR, "shl-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info DIV_LONG = + new Info(Opcodes.DIV_LONG, "div-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SHR_INT_2ADDR = - new Info(Opcodes.SHR_INT_2ADDR, "shr-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info REM_LONG = + new Info(Opcodes.REM_LONG, "rem-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info USHR_INT_2ADDR = - new Info(Opcodes.USHR_INT_2ADDR, "ushr-int/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info AND_LONG = + new Info(Opcodes.AND_LONG, "and-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info ADD_LONG_2ADDR = - new Info(Opcodes.ADD_LONG_2ADDR, "add-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info OR_LONG = + new Info(Opcodes.OR_LONG, "or-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SUB_LONG_2ADDR = - new Info(Opcodes.SUB_LONG_2ADDR, "sub-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info XOR_LONG = + new Info(Opcodes.XOR_LONG, "xor-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info MUL_LONG_2ADDR = - new Info(Opcodes.MUL_LONG_2ADDR, "mul-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SHL_LONG = + new Info(Opcodes.SHL_LONG, "shl-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info DIV_LONG_2ADDR = - new Info(Opcodes.DIV_LONG_2ADDR, "div-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SHR_LONG = + new Info(Opcodes.SHR_LONG, "shr-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info REM_LONG_2ADDR = - new Info(Opcodes.REM_LONG_2ADDR, "rem-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info USHR_LONG = + new Info(Opcodes.USHR_LONG, "ushr-long", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info AND_LONG_2ADDR = - new Info(Opcodes.AND_LONG_2ADDR, "and-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info ADD_FLOAT = + new Info(Opcodes.ADD_FLOAT, "add-float", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info OR_LONG_2ADDR = - new Info(Opcodes.OR_LONG_2ADDR, "or-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SUB_FLOAT = + new Info(Opcodes.SUB_FLOAT, "sub-float", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info XOR_LONG_2ADDR = - new Info(Opcodes.XOR_LONG_2ADDR, "xor-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info MUL_FLOAT = + new Info(Opcodes.MUL_FLOAT, "mul-float", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SHL_LONG_2ADDR = - new Info(Opcodes.SHL_LONG_2ADDR, "shl-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info DIV_FLOAT = + new Info(Opcodes.DIV_FLOAT, "div-float", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SHR_LONG_2ADDR = - new Info(Opcodes.SHR_LONG_2ADDR, "shr-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info REM_FLOAT = + new Info(Opcodes.REM_FLOAT, "rem-float", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info USHR_LONG_2ADDR = - new Info(Opcodes.USHR_LONG_2ADDR, "ushr-long/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info ADD_DOUBLE = + new Info(Opcodes.ADD_DOUBLE, "add-double", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info ADD_FLOAT_2ADDR = - new Info(Opcodes.ADD_FLOAT_2ADDR, "add-float/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SUB_DOUBLE = + new Info(Opcodes.SUB_DOUBLE, "sub-double", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info SUB_FLOAT_2ADDR = - new Info(Opcodes.SUB_FLOAT_2ADDR, "sub-float/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info MUL_DOUBLE = + new Info(Opcodes.MUL_DOUBLE, "mul-double", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info MUL_FLOAT_2ADDR = - new Info(Opcodes.MUL_FLOAT_2ADDR, "mul-float/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info DIV_DOUBLE = + new Info(Opcodes.DIV_DOUBLE, "div-double", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info DIV_FLOAT_2ADDR = - new Info(Opcodes.DIV_FLOAT_2ADDR, "div-float/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info REM_DOUBLE = + new Info(Opcodes.REM_DOUBLE, "rem-double", InstructionCodec.FORMAT_23X, IndexType.NONE); - public static final Info REM_FLOAT_2ADDR = - new Info(Opcodes.REM_FLOAT_2ADDR, "rem-float/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info ADD_INT_2ADDR = + new Info(Opcodes.ADD_INT_2ADDR, "add-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info ADD_DOUBLE_2ADDR = - new Info(Opcodes.ADD_DOUBLE_2ADDR, "add-double/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info SUB_INT_2ADDR = + new Info(Opcodes.SUB_INT_2ADDR, "sub-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info SUB_DOUBLE_2ADDR = - new Info(Opcodes.SUB_DOUBLE_2ADDR, "sub-double/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info MUL_INT_2ADDR = + new Info(Opcodes.MUL_INT_2ADDR, "mul-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info MUL_DOUBLE_2ADDR = - new Info(Opcodes.MUL_DOUBLE_2ADDR, "mul-double/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info DIV_INT_2ADDR = + new Info(Opcodes.DIV_INT_2ADDR, "div-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info DIV_DOUBLE_2ADDR = - new Info(Opcodes.DIV_DOUBLE_2ADDR, "div-double/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); + public static final Info REM_INT_2ADDR = + new Info(Opcodes.REM_INT_2ADDR, "rem-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); - public static final Info REM_DOUBLE_2ADDR = - new Info(Opcodes.REM_DOUBLE_2ADDR, "rem-double/2addr", - InstructionCodec.FORMAT_12X, IndexType.NONE); - - public static final Info ADD_INT_LIT16 = - new Info(Opcodes.ADD_INT_LIT16, "add-int/lit16", - InstructionCodec.FORMAT_22S, IndexType.NONE); - - public static final Info RSUB_INT = - new Info(Opcodes.RSUB_INT, "rsub-int", - InstructionCodec.FORMAT_22S, IndexType.NONE); - - public static final Info MUL_INT_LIT16 = - new Info(Opcodes.MUL_INT_LIT16, "mul-int/lit16", - InstructionCodec.FORMAT_22S, IndexType.NONE); - - public static final Info DIV_INT_LIT16 = - new Info(Opcodes.DIV_INT_LIT16, "div-int/lit16", - InstructionCodec.FORMAT_22S, IndexType.NONE); - - public static final Info REM_INT_LIT16 = - new Info(Opcodes.REM_INT_LIT16, "rem-int/lit16", - InstructionCodec.FORMAT_22S, IndexType.NONE); - - public static final Info AND_INT_LIT16 = - new Info(Opcodes.AND_INT_LIT16, "and-int/lit16", - InstructionCodec.FORMAT_22S, IndexType.NONE); - - public static final Info OR_INT_LIT16 = - new Info(Opcodes.OR_INT_LIT16, "or-int/lit16", - InstructionCodec.FORMAT_22S, IndexType.NONE); - - public static final Info XOR_INT_LIT16 = - new Info(Opcodes.XOR_INT_LIT16, "xor-int/lit16", - InstructionCodec.FORMAT_22S, IndexType.NONE); - - public static final Info ADD_INT_LIT8 = - new Info(Opcodes.ADD_INT_LIT8, "add-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info RSUB_INT_LIT8 = - new Info(Opcodes.RSUB_INT_LIT8, "rsub-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info MUL_INT_LIT8 = - new Info(Opcodes.MUL_INT_LIT8, "mul-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info DIV_INT_LIT8 = - new Info(Opcodes.DIV_INT_LIT8, "div-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info REM_INT_LIT8 = - new Info(Opcodes.REM_INT_LIT8, "rem-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info AND_INT_LIT8 = - new Info(Opcodes.AND_INT_LIT8, "and-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info OR_INT_LIT8 = - new Info(Opcodes.OR_INT_LIT8, "or-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info XOR_INT_LIT8 = - new Info(Opcodes.XOR_INT_LIT8, "xor-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info SHL_INT_LIT8 = - new Info(Opcodes.SHL_INT_LIT8, "shl-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info SHR_INT_LIT8 = - new Info(Opcodes.SHR_INT_LIT8, "shr-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - public static final Info USHR_INT_LIT8 = - new Info(Opcodes.USHR_INT_LIT8, "ushr-int/lit8", - InstructionCodec.FORMAT_22B, IndexType.NONE); - - // END(opcode-info-defs) - - // Static initialization. - static { - INFO = new Info[Opcodes.MAX_VALUE - Opcodes.MIN_VALUE + 1]; - - // TODO: Stop using this constant. - set(SPECIAL_FORMAT); - - // TODO: These payload opcodes should be generated by opcode-gen. - set(PACKED_SWITCH_PAYLOAD); - set(SPARSE_SWITCH_PAYLOAD); - set(FILL_ARRAY_DATA_PAYLOAD); - - // BEGIN(opcode-info-init); GENERATED AUTOMATICALLY BY opcode-gen - set(NOP); - set(MOVE); - set(MOVE_FROM16); - set(MOVE_16); - set(MOVE_WIDE); - set(MOVE_WIDE_FROM16); - set(MOVE_WIDE_16); - set(MOVE_OBJECT); - set(MOVE_OBJECT_FROM16); - set(MOVE_OBJECT_16); - set(MOVE_RESULT); - set(MOVE_RESULT_WIDE); - set(MOVE_RESULT_OBJECT); - set(MOVE_EXCEPTION); - set(RETURN_VOID); - set(RETURN); - set(RETURN_WIDE); - set(RETURN_OBJECT); - set(CONST_4); - set(CONST_16); - set(CONST); - set(CONST_HIGH16); - set(CONST_WIDE_16); - set(CONST_WIDE_32); - set(CONST_WIDE); - set(CONST_WIDE_HIGH16); - set(CONST_STRING); - set(CONST_STRING_JUMBO); - set(CONST_CLASS); - set(MONITOR_ENTER); - set(MONITOR_EXIT); - set(CHECK_CAST); - set(INSTANCE_OF); - set(ARRAY_LENGTH); - set(NEW_INSTANCE); - set(NEW_ARRAY); - set(FILLED_NEW_ARRAY); - set(FILLED_NEW_ARRAY_RANGE); - set(FILL_ARRAY_DATA); - set(THROW); - set(GOTO); - set(GOTO_16); - set(GOTO_32); - set(PACKED_SWITCH); - set(SPARSE_SWITCH); - set(CMPL_FLOAT); - set(CMPG_FLOAT); - set(CMPL_DOUBLE); - set(CMPG_DOUBLE); - set(CMP_LONG); - set(IF_EQ); - set(IF_NE); - set(IF_LT); - set(IF_GE); - set(IF_GT); - set(IF_LE); - set(IF_EQZ); - set(IF_NEZ); - set(IF_LTZ); - set(IF_GEZ); - set(IF_GTZ); - set(IF_LEZ); - set(AGET); - set(AGET_WIDE); - set(AGET_OBJECT); - set(AGET_BOOLEAN); - set(AGET_BYTE); - set(AGET_CHAR); - set(AGET_SHORT); - set(APUT); - set(APUT_WIDE); - set(APUT_OBJECT); - set(APUT_BOOLEAN); - set(APUT_BYTE); - set(APUT_CHAR); - set(APUT_SHORT); - set(IGET); - set(IGET_WIDE); - set(IGET_OBJECT); - set(IGET_BOOLEAN); - set(IGET_BYTE); - set(IGET_CHAR); - set(IGET_SHORT); - set(IPUT); - set(IPUT_WIDE); - set(IPUT_OBJECT); - set(IPUT_BOOLEAN); - set(IPUT_BYTE); - set(IPUT_CHAR); - set(IPUT_SHORT); - set(SGET); - set(SGET_WIDE); - set(SGET_OBJECT); - set(SGET_BOOLEAN); - set(SGET_BYTE); - set(SGET_CHAR); - set(SGET_SHORT); - set(SPUT); - set(SPUT_WIDE); - set(SPUT_OBJECT); - set(SPUT_BOOLEAN); - set(SPUT_BYTE); - set(SPUT_CHAR); - set(SPUT_SHORT); - set(INVOKE_VIRTUAL); - set(INVOKE_SUPER); - set(INVOKE_DIRECT); - set(INVOKE_STATIC); - set(INVOKE_INTERFACE); - set(INVOKE_VIRTUAL_RANGE); - set(INVOKE_SUPER_RANGE); - set(INVOKE_DIRECT_RANGE); - set(INVOKE_STATIC_RANGE); - set(INVOKE_INTERFACE_RANGE); - set(NEG_INT); - set(NOT_INT); - set(NEG_LONG); - set(NOT_LONG); - set(NEG_FLOAT); - set(NEG_DOUBLE); - set(INT_TO_LONG); - set(INT_TO_FLOAT); - set(INT_TO_DOUBLE); - set(LONG_TO_INT); - set(LONG_TO_FLOAT); - set(LONG_TO_DOUBLE); - set(FLOAT_TO_INT); - set(FLOAT_TO_LONG); - set(FLOAT_TO_DOUBLE); - set(DOUBLE_TO_INT); - set(DOUBLE_TO_LONG); - set(DOUBLE_TO_FLOAT); - set(INT_TO_BYTE); - set(INT_TO_CHAR); - set(INT_TO_SHORT); - set(ADD_INT); - set(SUB_INT); - set(MUL_INT); - set(DIV_INT); - set(REM_INT); - set(AND_INT); - set(OR_INT); - set(XOR_INT); - set(SHL_INT); - set(SHR_INT); - set(USHR_INT); - set(ADD_LONG); - set(SUB_LONG); - set(MUL_LONG); - set(DIV_LONG); - set(REM_LONG); - set(AND_LONG); - set(OR_LONG); - set(XOR_LONG); - set(SHL_LONG); - set(SHR_LONG); - set(USHR_LONG); - set(ADD_FLOAT); - set(SUB_FLOAT); - set(MUL_FLOAT); - set(DIV_FLOAT); - set(REM_FLOAT); - set(ADD_DOUBLE); - set(SUB_DOUBLE); - set(MUL_DOUBLE); - set(DIV_DOUBLE); - set(REM_DOUBLE); - set(ADD_INT_2ADDR); - set(SUB_INT_2ADDR); - set(MUL_INT_2ADDR); - set(DIV_INT_2ADDR); - set(REM_INT_2ADDR); - set(AND_INT_2ADDR); - set(OR_INT_2ADDR); - set(XOR_INT_2ADDR); - set(SHL_INT_2ADDR); - set(SHR_INT_2ADDR); - set(USHR_INT_2ADDR); - set(ADD_LONG_2ADDR); - set(SUB_LONG_2ADDR); - set(MUL_LONG_2ADDR); - set(DIV_LONG_2ADDR); - set(REM_LONG_2ADDR); - set(AND_LONG_2ADDR); - set(OR_LONG_2ADDR); - set(XOR_LONG_2ADDR); - set(SHL_LONG_2ADDR); - set(SHR_LONG_2ADDR); - set(USHR_LONG_2ADDR); - set(ADD_FLOAT_2ADDR); - set(SUB_FLOAT_2ADDR); - set(MUL_FLOAT_2ADDR); - set(DIV_FLOAT_2ADDR); - set(REM_FLOAT_2ADDR); - set(ADD_DOUBLE_2ADDR); - set(SUB_DOUBLE_2ADDR); - set(MUL_DOUBLE_2ADDR); - set(DIV_DOUBLE_2ADDR); - set(REM_DOUBLE_2ADDR); - set(ADD_INT_LIT16); - set(RSUB_INT); - set(MUL_INT_LIT16); - set(DIV_INT_LIT16); - set(REM_INT_LIT16); - set(AND_INT_LIT16); - set(OR_INT_LIT16); - set(XOR_INT_LIT16); - set(ADD_INT_LIT8); - set(RSUB_INT_LIT8); - set(MUL_INT_LIT8); - set(DIV_INT_LIT8); - set(REM_INT_LIT8); - set(AND_INT_LIT8); - set(OR_INT_LIT8); - set(XOR_INT_LIT8); - set(SHL_INT_LIT8); - set(SHR_INT_LIT8); - set(USHR_INT_LIT8); - // END(opcode-info-init) - } + public static final Info AND_INT_2ADDR = + new Info(Opcodes.AND_INT_2ADDR, "and-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); - /** - * This class is uninstantiable. - */ - private OpcodeInfo() { - // This space intentionally left blank. - } + public static final Info OR_INT_2ADDR = + new Info(Opcodes.OR_INT_2ADDR, "or-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info XOR_INT_2ADDR = + new Info(Opcodes.XOR_INT_2ADDR, "xor-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info SHL_INT_2ADDR = + new Info(Opcodes.SHL_INT_2ADDR, "shl-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info SHR_INT_2ADDR = + new Info(Opcodes.SHR_INT_2ADDR, "shr-int/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info USHR_INT_2ADDR = new Info(Opcodes.USHR_INT_2ADDR, "ushr-int/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info ADD_LONG_2ADDR = new Info(Opcodes.ADD_LONG_2ADDR, "add-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info SUB_LONG_2ADDR = new Info(Opcodes.SUB_LONG_2ADDR, "sub-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info MUL_LONG_2ADDR = new Info(Opcodes.MUL_LONG_2ADDR, "mul-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info DIV_LONG_2ADDR = new Info(Opcodes.DIV_LONG_2ADDR, "div-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info REM_LONG_2ADDR = new Info(Opcodes.REM_LONG_2ADDR, "rem-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info AND_LONG_2ADDR = new Info(Opcodes.AND_LONG_2ADDR, "and-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info OR_LONG_2ADDR = + new Info(Opcodes.OR_LONG_2ADDR, "or-long/2addr", InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info XOR_LONG_2ADDR = new Info(Opcodes.XOR_LONG_2ADDR, "xor-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info SHL_LONG_2ADDR = new Info(Opcodes.SHL_LONG_2ADDR, "shl-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info SHR_LONG_2ADDR = new Info(Opcodes.SHR_LONG_2ADDR, "shr-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info USHR_LONG_2ADDR = new Info(Opcodes.USHR_LONG_2ADDR, "ushr-long/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info ADD_FLOAT_2ADDR = new Info(Opcodes.ADD_FLOAT_2ADDR, "add-float/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info SUB_FLOAT_2ADDR = new Info(Opcodes.SUB_FLOAT_2ADDR, "sub-float/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info MUL_FLOAT_2ADDR = new Info(Opcodes.MUL_FLOAT_2ADDR, "mul-float/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info DIV_FLOAT_2ADDR = new Info(Opcodes.DIV_FLOAT_2ADDR, "div-float/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info REM_FLOAT_2ADDR = new Info(Opcodes.REM_FLOAT_2ADDR, "rem-float/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info ADD_DOUBLE_2ADDR = new Info(Opcodes.ADD_DOUBLE_2ADDR, "add-double/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info SUB_DOUBLE_2ADDR = new Info(Opcodes.SUB_DOUBLE_2ADDR, "sub-double/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info MUL_DOUBLE_2ADDR = new Info(Opcodes.MUL_DOUBLE_2ADDR, "mul-double/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info DIV_DOUBLE_2ADDR = new Info(Opcodes.DIV_DOUBLE_2ADDR, "div-double/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info REM_DOUBLE_2ADDR = new Info(Opcodes.REM_DOUBLE_2ADDR, "rem-double/2addr", + InstructionCodec.FORMAT_12X, IndexType.NONE); + + public static final Info ADD_INT_LIT16 = + new Info(Opcodes.ADD_INT_LIT16, "add-int/lit16", InstructionCodec.FORMAT_22S, IndexType.NONE); + + public static final Info RSUB_INT = + new Info(Opcodes.RSUB_INT, "rsub-int", InstructionCodec.FORMAT_22S, IndexType.NONE); + + public static final Info MUL_INT_LIT16 = + new Info(Opcodes.MUL_INT_LIT16, "mul-int/lit16", InstructionCodec.FORMAT_22S, IndexType.NONE); + + public static final Info DIV_INT_LIT16 = + new Info(Opcodes.DIV_INT_LIT16, "div-int/lit16", InstructionCodec.FORMAT_22S, IndexType.NONE); + + public static final Info REM_INT_LIT16 = + new Info(Opcodes.REM_INT_LIT16, "rem-int/lit16", InstructionCodec.FORMAT_22S, IndexType.NONE); + + public static final Info AND_INT_LIT16 = + new Info(Opcodes.AND_INT_LIT16, "and-int/lit16", InstructionCodec.FORMAT_22S, IndexType.NONE); + + public static final Info OR_INT_LIT16 = + new Info(Opcodes.OR_INT_LIT16, "or-int/lit16", InstructionCodec.FORMAT_22S, IndexType.NONE); - /** - * Gets the {@link @Info} for the given opcode value. - * - * @param opcode {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the - * opcode value - * @return non-null; the associated opcode information instance - */ - public static Info get(int opcode) { - int idx = opcode - Opcodes.MIN_VALUE; - - try { - Info result = INFO[idx]; - if (result != null) { - return result; - } - } catch (ArrayIndexOutOfBoundsException ex) { - // Fall through. - } - - throw new IllegalArgumentException("bogus opcode: " - + Hex.u2or4(opcode)); + public static final Info XOR_INT_LIT16 = + new Info(Opcodes.XOR_INT_LIT16, "xor-int/lit16", InstructionCodec.FORMAT_22S, IndexType.NONE); + + public static final Info ADD_INT_LIT8 = + new Info(Opcodes.ADD_INT_LIT8, "add-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info RSUB_INT_LIT8 = + new Info(Opcodes.RSUB_INT_LIT8, "rsub-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info MUL_INT_LIT8 = + new Info(Opcodes.MUL_INT_LIT8, "mul-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info DIV_INT_LIT8 = + new Info(Opcodes.DIV_INT_LIT8, "div-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info REM_INT_LIT8 = + new Info(Opcodes.REM_INT_LIT8, "rem-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info AND_INT_LIT8 = + new Info(Opcodes.AND_INT_LIT8, "and-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info OR_INT_LIT8 = + new Info(Opcodes.OR_INT_LIT8, "or-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info XOR_INT_LIT8 = + new Info(Opcodes.XOR_INT_LIT8, "xor-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info SHL_INT_LIT8 = + new Info(Opcodes.SHL_INT_LIT8, "shl-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info SHR_INT_LIT8 = + new Info(Opcodes.SHR_INT_LIT8, "shr-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + public static final Info USHR_INT_LIT8 = + new Info(Opcodes.USHR_INT_LIT8, "ushr-int/lit8", InstructionCodec.FORMAT_22B, IndexType.NONE); + + // END(opcode-info-defs) + + // Static initialization. + static { + INFO = new Info[Opcodes.MAX_VALUE - Opcodes.MIN_VALUE + 1]; + + // TODO(dx team): Stop using this constant. + set(SPECIAL_FORMAT); + + // TODO(dx team): These payload opcodes should be generated by opcode-gen. + set(PACKED_SWITCH_PAYLOAD); + set(SPARSE_SWITCH_PAYLOAD); + set(FILL_ARRAY_DATA_PAYLOAD); + + // BEGIN(opcode-info-init); GENERATED AUTOMATICALLY BY opcode-gen + set(NOP); + set(MOVE); + set(MOVE_FROM16); + set(MOVE_16); + set(MOVE_WIDE); + set(MOVE_WIDE_FROM16); + set(MOVE_WIDE_16); + set(MOVE_OBJECT); + set(MOVE_OBJECT_FROM16); + set(MOVE_OBJECT_16); + set(MOVE_RESULT); + set(MOVE_RESULT_WIDE); + set(MOVE_RESULT_OBJECT); + set(MOVE_EXCEPTION); + set(RETURN_VOID); + set(RETURN); + set(RETURN_WIDE); + set(RETURN_OBJECT); + set(CONST_4); + set(CONST_16); + set(CONST); + set(CONST_HIGH16); + set(CONST_WIDE_16); + set(CONST_WIDE_32); + set(CONST_WIDE); + set(CONST_WIDE_HIGH16); + set(CONST_STRING); + set(CONST_STRING_JUMBO); + set(CONST_CLASS); + set(MONITOR_ENTER); + set(MONITOR_EXIT); + set(CHECK_CAST); + set(INSTANCE_OF); + set(ARRAY_LENGTH); + set(NEW_INSTANCE); + set(NEW_ARRAY); + set(FILLED_NEW_ARRAY); + set(FILLED_NEW_ARRAY_RANGE); + set(FILL_ARRAY_DATA); + set(THROW); + set(GOTO); + set(GOTO_16); + set(GOTO_32); + set(PACKED_SWITCH); + set(SPARSE_SWITCH); + set(CMPL_FLOAT); + set(CMPG_FLOAT); + set(CMPL_DOUBLE); + set(CMPG_DOUBLE); + set(CMP_LONG); + set(IF_EQ); + set(IF_NE); + set(IF_LT); + set(IF_GE); + set(IF_GT); + set(IF_LE); + set(IF_EQZ); + set(IF_NEZ); + set(IF_LTZ); + set(IF_GEZ); + set(IF_GTZ); + set(IF_LEZ); + set(AGET); + set(AGET_WIDE); + set(AGET_OBJECT); + set(AGET_BOOLEAN); + set(AGET_BYTE); + set(AGET_CHAR); + set(AGET_SHORT); + set(APUT); + set(APUT_WIDE); + set(APUT_OBJECT); + set(APUT_BOOLEAN); + set(APUT_BYTE); + set(APUT_CHAR); + set(APUT_SHORT); + set(IGET); + set(IGET_WIDE); + set(IGET_OBJECT); + set(IGET_BOOLEAN); + set(IGET_BYTE); + set(IGET_CHAR); + set(IGET_SHORT); + set(IPUT); + set(IPUT_WIDE); + set(IPUT_OBJECT); + set(IPUT_BOOLEAN); + set(IPUT_BYTE); + set(IPUT_CHAR); + set(IPUT_SHORT); + set(SGET); + set(SGET_WIDE); + set(SGET_OBJECT); + set(SGET_BOOLEAN); + set(SGET_BYTE); + set(SGET_CHAR); + set(SGET_SHORT); + set(SPUT); + set(SPUT_WIDE); + set(SPUT_OBJECT); + set(SPUT_BOOLEAN); + set(SPUT_BYTE); + set(SPUT_CHAR); + set(SPUT_SHORT); + set(INVOKE_VIRTUAL); + set(INVOKE_SUPER); + set(INVOKE_DIRECT); + set(INVOKE_STATIC); + set(INVOKE_INTERFACE); + set(INVOKE_VIRTUAL_RANGE); + set(INVOKE_SUPER_RANGE); + set(INVOKE_DIRECT_RANGE); + set(INVOKE_STATIC_RANGE); + set(INVOKE_INTERFACE_RANGE); + set(NEG_INT); + set(NOT_INT); + set(NEG_LONG); + set(NOT_LONG); + set(NEG_FLOAT); + set(NEG_DOUBLE); + set(INT_TO_LONG); + set(INT_TO_FLOAT); + set(INT_TO_DOUBLE); + set(LONG_TO_INT); + set(LONG_TO_FLOAT); + set(LONG_TO_DOUBLE); + set(FLOAT_TO_INT); + set(FLOAT_TO_LONG); + set(FLOAT_TO_DOUBLE); + set(DOUBLE_TO_INT); + set(DOUBLE_TO_LONG); + set(DOUBLE_TO_FLOAT); + set(INT_TO_BYTE); + set(INT_TO_CHAR); + set(INT_TO_SHORT); + set(ADD_INT); + set(SUB_INT); + set(MUL_INT); + set(DIV_INT); + set(REM_INT); + set(AND_INT); + set(OR_INT); + set(XOR_INT); + set(SHL_INT); + set(SHR_INT); + set(USHR_INT); + set(ADD_LONG); + set(SUB_LONG); + set(MUL_LONG); + set(DIV_LONG); + set(REM_LONG); + set(AND_LONG); + set(OR_LONG); + set(XOR_LONG); + set(SHL_LONG); + set(SHR_LONG); + set(USHR_LONG); + set(ADD_FLOAT); + set(SUB_FLOAT); + set(MUL_FLOAT); + set(DIV_FLOAT); + set(REM_FLOAT); + set(ADD_DOUBLE); + set(SUB_DOUBLE); + set(MUL_DOUBLE); + set(DIV_DOUBLE); + set(REM_DOUBLE); + set(ADD_INT_2ADDR); + set(SUB_INT_2ADDR); + set(MUL_INT_2ADDR); + set(DIV_INT_2ADDR); + set(REM_INT_2ADDR); + set(AND_INT_2ADDR); + set(OR_INT_2ADDR); + set(XOR_INT_2ADDR); + set(SHL_INT_2ADDR); + set(SHR_INT_2ADDR); + set(USHR_INT_2ADDR); + set(ADD_LONG_2ADDR); + set(SUB_LONG_2ADDR); + set(MUL_LONG_2ADDR); + set(DIV_LONG_2ADDR); + set(REM_LONG_2ADDR); + set(AND_LONG_2ADDR); + set(OR_LONG_2ADDR); + set(XOR_LONG_2ADDR); + set(SHL_LONG_2ADDR); + set(SHR_LONG_2ADDR); + set(USHR_LONG_2ADDR); + set(ADD_FLOAT_2ADDR); + set(SUB_FLOAT_2ADDR); + set(MUL_FLOAT_2ADDR); + set(DIV_FLOAT_2ADDR); + set(REM_FLOAT_2ADDR); + set(ADD_DOUBLE_2ADDR); + set(SUB_DOUBLE_2ADDR); + set(MUL_DOUBLE_2ADDR); + set(DIV_DOUBLE_2ADDR); + set(REM_DOUBLE_2ADDR); + set(ADD_INT_LIT16); + set(RSUB_INT); + set(MUL_INT_LIT16); + set(DIV_INT_LIT16); + set(REM_INT_LIT16); + set(AND_INT_LIT16); + set(OR_INT_LIT16); + set(XOR_INT_LIT16); + set(ADD_INT_LIT8); + set(RSUB_INT_LIT8); + set(MUL_INT_LIT8); + set(DIV_INT_LIT8); + set(REM_INT_LIT8); + set(AND_INT_LIT8); + set(OR_INT_LIT8); + set(XOR_INT_LIT8); + set(SHL_INT_LIT8); + set(SHR_INT_LIT8); + set(USHR_INT_LIT8); + // END(opcode-info-init) + } + + /** + * This class is uninstantiable. + */ + private OpcodeInfo() { + // This space intentionally left blank. + } + + /** + * Gets the {@link @Info} for the given opcode value. + * + * @param opcode {@code Opcodes.MIN_VALUE..Opcodes.MAX_VALUE;} the + * opcode value + * @return non-null; the associated opcode information instance + */ + public static Info get(int opcode) { + int idx = opcode - Opcodes.MIN_VALUE; + + try { + Info result = INFO[idx]; + if (result != null) { + return result; + } + } catch (ArrayIndexOutOfBoundsException ex) { + // Fall through. } - /** - * Gets the name of the given opcode. - */ - public static String getName(int opcode) { - return get(opcode).getName(); + throw new IllegalArgumentException("bogus opcode: " + Hex.u2or4(opcode)); + } + + /** + * Gets the name of the given opcode. + */ + public static String getName(int opcode) { + return get(opcode).getName(); + } + + /** + * Gets the format (an {@link InstructionCodec}) for the given opcode + * value. + */ + public static InstructionCodec getFormat(int opcode) { + return get(opcode).getFormat(); + } + + /** + * Gets the {@link IndexType} for the given opcode value. + */ + public static IndexType getIndexType(int opcode) { + return get(opcode).getIndexType(); + } + + /** + * Puts the given opcode into the table of all ops. + * + * @param opcode non-null; the opcode + */ + private static void set(Info opcode) { + int idx = opcode.getOpcode() - Opcodes.MIN_VALUE; + INFO[idx] = opcode; + } + + /** + * Information about an opcode. + */ + public static class Info { + private final int opcode; + private final String name; + private final InstructionCodec format; + private final IndexType indexType; + + public Info(int opcode, String name, InstructionCodec format, IndexType indexType) { + this.opcode = opcode; + this.name = name; + this.format = format; + this.indexType = indexType; } - /** - * Gets the format (an {@link InstructionCodec}) for the given opcode - * value. - */ - public static InstructionCodec getFormat(int opcode) { - return get(opcode).getFormat(); + public int getOpcode() { + return opcode; } - /** - * Gets the {@link IndexType} for the given opcode value. - */ - public static IndexType getIndexType(int opcode) { - return get(opcode).getIndexType(); + public String getName() { + return name; } - /** - * Puts the given opcode into the table of all ops. - * - * @param opcode non-null; the opcode - */ - private static void set(Info opcode) { - int idx = opcode.getOpcode() - Opcodes.MIN_VALUE; - INFO[idx] = opcode; + public InstructionCodec getFormat() { + return format; } - /** - * Information about an opcode. - */ - public static class Info { - private final int opcode; - private final String name; - private final InstructionCodec format; - private final IndexType indexType; - - public Info(int opcode, String name, InstructionCodec format, - IndexType indexType) { - this.opcode = opcode; - this.name = name; - this.format = format; - this.indexType = indexType; - } - - public int getOpcode() { - return opcode; - } - - public String getName() { - return name; - } - - public InstructionCodec getFormat() { - return format; - } - - public IndexType getIndexType() { - return indexType; - } + public IndexType getIndexType() { + return indexType; } + } } diff --git a/dx/src/com/android/jack/dx/io/Opcodes.java b/dx/src/com/android/jack/dx/io/Opcodes.java index 77b5d8e..4f79a31 100644 --- a/dx/src/com/android/jack/dx/io/Opcodes.java +++ b/dx/src/com/android/jack/dx/io/Opcodes.java @@ -21,326 +21,326 @@ package com.android.jack.dx.io; * document for the meaning and instruction format of each opcode. */ public final class Opcodes { - /** - * pseudo-opcode used for nonstandard format payload "instructions". TODO: - * Retire this concept, and start treating the payload instructions - * more like the rest. - */ - public static final int SPECIAL_FORMAT = -1; + /** + * pseudo-opcode used for nonstandard format payload "instructions". TODO(dx team): + * Retire this concept, and start treating the payload instructions + * more like the rest. + */ + public static final int SPECIAL_FORMAT = -1; - /** - * pseudo-opcode used to indicate there is no next opcode; used - * in opcode chaining lists - */ - public static final int NO_NEXT = -1; + /** + * pseudo-opcode used to indicate there is no next opcode; used + * in opcode chaining lists + */ + public static final int NO_NEXT = -1; - /** minimum valid opcode value */ - public static final int MIN_VALUE = -1; + /** minimum valid opcode value */ + public static final int MIN_VALUE = -1; - /** maximum valid opcode value */ - public static final int MAX_VALUE = 0xffff; + /** maximum valid opcode value */ + public static final int MAX_VALUE = 0xffff; - // BEGIN(opcodes); GENERATED AUTOMATICALLY BY opcode-gen - public static final int NOP = 0x00; - public static final int MOVE = 0x01; - public static final int MOVE_FROM16 = 0x02; - public static final int MOVE_16 = 0x03; - public static final int MOVE_WIDE = 0x04; - public static final int MOVE_WIDE_FROM16 = 0x05; - public static final int MOVE_WIDE_16 = 0x06; - public static final int MOVE_OBJECT = 0x07; - public static final int MOVE_OBJECT_FROM16 = 0x08; - public static final int MOVE_OBJECT_16 = 0x09; - public static final int MOVE_RESULT = 0x0a; - public static final int MOVE_RESULT_WIDE = 0x0b; - public static final int MOVE_RESULT_OBJECT = 0x0c; - public static final int MOVE_EXCEPTION = 0x0d; - public static final int RETURN_VOID = 0x0e; - public static final int RETURN = 0x0f; - public static final int RETURN_WIDE = 0x10; - public static final int RETURN_OBJECT = 0x11; - public static final int CONST_4 = 0x12; - public static final int CONST_16 = 0x13; - public static final int CONST = 0x14; - public static final int CONST_HIGH16 = 0x15; - public static final int CONST_WIDE_16 = 0x16; - public static final int CONST_WIDE_32 = 0x17; - public static final int CONST_WIDE = 0x18; - public static final int CONST_WIDE_HIGH16 = 0x19; - public static final int CONST_STRING = 0x1a; - public static final int CONST_STRING_JUMBO = 0x1b; - public static final int CONST_CLASS = 0x1c; - public static final int MONITOR_ENTER = 0x1d; - public static final int MONITOR_EXIT = 0x1e; - public static final int CHECK_CAST = 0x1f; - public static final int INSTANCE_OF = 0x20; - public static final int ARRAY_LENGTH = 0x21; - public static final int NEW_INSTANCE = 0x22; - public static final int NEW_ARRAY = 0x23; - public static final int FILLED_NEW_ARRAY = 0x24; - public static final int FILLED_NEW_ARRAY_RANGE = 0x25; - public static final int FILL_ARRAY_DATA = 0x26; - public static final int THROW = 0x27; - public static final int GOTO = 0x28; - public static final int GOTO_16 = 0x29; - public static final int GOTO_32 = 0x2a; - public static final int PACKED_SWITCH = 0x2b; - public static final int SPARSE_SWITCH = 0x2c; - public static final int CMPL_FLOAT = 0x2d; - public static final int CMPG_FLOAT = 0x2e; - public static final int CMPL_DOUBLE = 0x2f; - public static final int CMPG_DOUBLE = 0x30; - public static final int CMP_LONG = 0x31; - public static final int IF_EQ = 0x32; - public static final int IF_NE = 0x33; - public static final int IF_LT = 0x34; - public static final int IF_GE = 0x35; - public static final int IF_GT = 0x36; - public static final int IF_LE = 0x37; - public static final int IF_EQZ = 0x38; - public static final int IF_NEZ = 0x39; - public static final int IF_LTZ = 0x3a; - public static final int IF_GEZ = 0x3b; - public static final int IF_GTZ = 0x3c; - public static final int IF_LEZ = 0x3d; - public static final int AGET = 0x44; - public static final int AGET_WIDE = 0x45; - public static final int AGET_OBJECT = 0x46; - public static final int AGET_BOOLEAN = 0x47; - public static final int AGET_BYTE = 0x48; - public static final int AGET_CHAR = 0x49; - public static final int AGET_SHORT = 0x4a; - public static final int APUT = 0x4b; - public static final int APUT_WIDE = 0x4c; - public static final int APUT_OBJECT = 0x4d; - public static final int APUT_BOOLEAN = 0x4e; - public static final int APUT_BYTE = 0x4f; - public static final int APUT_CHAR = 0x50; - public static final int APUT_SHORT = 0x51; - public static final int IGET = 0x52; - public static final int IGET_WIDE = 0x53; - public static final int IGET_OBJECT = 0x54; - public static final int IGET_BOOLEAN = 0x55; - public static final int IGET_BYTE = 0x56; - public static final int IGET_CHAR = 0x57; - public static final int IGET_SHORT = 0x58; - public static final int IPUT = 0x59; - public static final int IPUT_WIDE = 0x5a; - public static final int IPUT_OBJECT = 0x5b; - public static final int IPUT_BOOLEAN = 0x5c; - public static final int IPUT_BYTE = 0x5d; - public static final int IPUT_CHAR = 0x5e; - public static final int IPUT_SHORT = 0x5f; - public static final int SGET = 0x60; - public static final int SGET_WIDE = 0x61; - public static final int SGET_OBJECT = 0x62; - public static final int SGET_BOOLEAN = 0x63; - public static final int SGET_BYTE = 0x64; - public static final int SGET_CHAR = 0x65; - public static final int SGET_SHORT = 0x66; - public static final int SPUT = 0x67; - public static final int SPUT_WIDE = 0x68; - public static final int SPUT_OBJECT = 0x69; - public static final int SPUT_BOOLEAN = 0x6a; - public static final int SPUT_BYTE = 0x6b; - public static final int SPUT_CHAR = 0x6c; - public static final int SPUT_SHORT = 0x6d; - public static final int INVOKE_VIRTUAL = 0x6e; - public static final int INVOKE_SUPER = 0x6f; - public static final int INVOKE_DIRECT = 0x70; - public static final int INVOKE_STATIC = 0x71; - public static final int INVOKE_INTERFACE = 0x72; - public static final int INVOKE_VIRTUAL_RANGE = 0x74; - public static final int INVOKE_SUPER_RANGE = 0x75; - public static final int INVOKE_DIRECT_RANGE = 0x76; - public static final int INVOKE_STATIC_RANGE = 0x77; - public static final int INVOKE_INTERFACE_RANGE = 0x78; - public static final int NEG_INT = 0x7b; - public static final int NOT_INT = 0x7c; - public static final int NEG_LONG = 0x7d; - public static final int NOT_LONG = 0x7e; - public static final int NEG_FLOAT = 0x7f; - public static final int NEG_DOUBLE = 0x80; - public static final int INT_TO_LONG = 0x81; - public static final int INT_TO_FLOAT = 0x82; - public static final int INT_TO_DOUBLE = 0x83; - public static final int LONG_TO_INT = 0x84; - public static final int LONG_TO_FLOAT = 0x85; - public static final int LONG_TO_DOUBLE = 0x86; - public static final int FLOAT_TO_INT = 0x87; - public static final int FLOAT_TO_LONG = 0x88; - public static final int FLOAT_TO_DOUBLE = 0x89; - public static final int DOUBLE_TO_INT = 0x8a; - public static final int DOUBLE_TO_LONG = 0x8b; - public static final int DOUBLE_TO_FLOAT = 0x8c; - public static final int INT_TO_BYTE = 0x8d; - public static final int INT_TO_CHAR = 0x8e; - public static final int INT_TO_SHORT = 0x8f; - public static final int ADD_INT = 0x90; - public static final int SUB_INT = 0x91; - public static final int MUL_INT = 0x92; - public static final int DIV_INT = 0x93; - public static final int REM_INT = 0x94; - public static final int AND_INT = 0x95; - public static final int OR_INT = 0x96; - public static final int XOR_INT = 0x97; - public static final int SHL_INT = 0x98; - public static final int SHR_INT = 0x99; - public static final int USHR_INT = 0x9a; - public static final int ADD_LONG = 0x9b; - public static final int SUB_LONG = 0x9c; - public static final int MUL_LONG = 0x9d; - public static final int DIV_LONG = 0x9e; - public static final int REM_LONG = 0x9f; - public static final int AND_LONG = 0xa0; - public static final int OR_LONG = 0xa1; - public static final int XOR_LONG = 0xa2; - public static final int SHL_LONG = 0xa3; - public static final int SHR_LONG = 0xa4; - public static final int USHR_LONG = 0xa5; - public static final int ADD_FLOAT = 0xa6; - public static final int SUB_FLOAT = 0xa7; - public static final int MUL_FLOAT = 0xa8; - public static final int DIV_FLOAT = 0xa9; - public static final int REM_FLOAT = 0xaa; - public static final int ADD_DOUBLE = 0xab; - public static final int SUB_DOUBLE = 0xac; - public static final int MUL_DOUBLE = 0xad; - public static final int DIV_DOUBLE = 0xae; - public static final int REM_DOUBLE = 0xaf; - public static final int ADD_INT_2ADDR = 0xb0; - public static final int SUB_INT_2ADDR = 0xb1; - public static final int MUL_INT_2ADDR = 0xb2; - public static final int DIV_INT_2ADDR = 0xb3; - public static final int REM_INT_2ADDR = 0xb4; - public static final int AND_INT_2ADDR = 0xb5; - public static final int OR_INT_2ADDR = 0xb6; - public static final int XOR_INT_2ADDR = 0xb7; - public static final int SHL_INT_2ADDR = 0xb8; - public static final int SHR_INT_2ADDR = 0xb9; - public static final int USHR_INT_2ADDR = 0xba; - public static final int ADD_LONG_2ADDR = 0xbb; - public static final int SUB_LONG_2ADDR = 0xbc; - public static final int MUL_LONG_2ADDR = 0xbd; - public static final int DIV_LONG_2ADDR = 0xbe; - public static final int REM_LONG_2ADDR = 0xbf; - public static final int AND_LONG_2ADDR = 0xc0; - public static final int OR_LONG_2ADDR = 0xc1; - public static final int XOR_LONG_2ADDR = 0xc2; - public static final int SHL_LONG_2ADDR = 0xc3; - public static final int SHR_LONG_2ADDR = 0xc4; - public static final int USHR_LONG_2ADDR = 0xc5; - public static final int ADD_FLOAT_2ADDR = 0xc6; - public static final int SUB_FLOAT_2ADDR = 0xc7; - public static final int MUL_FLOAT_2ADDR = 0xc8; - public static final int DIV_FLOAT_2ADDR = 0xc9; - public static final int REM_FLOAT_2ADDR = 0xca; - public static final int ADD_DOUBLE_2ADDR = 0xcb; - public static final int SUB_DOUBLE_2ADDR = 0xcc; - public static final int MUL_DOUBLE_2ADDR = 0xcd; - public static final int DIV_DOUBLE_2ADDR = 0xce; - public static final int REM_DOUBLE_2ADDR = 0xcf; - public static final int ADD_INT_LIT16 = 0xd0; - public static final int RSUB_INT = 0xd1; - public static final int MUL_INT_LIT16 = 0xd2; - public static final int DIV_INT_LIT16 = 0xd3; - public static final int REM_INT_LIT16 = 0xd4; - public static final int AND_INT_LIT16 = 0xd5; - public static final int OR_INT_LIT16 = 0xd6; - public static final int XOR_INT_LIT16 = 0xd7; - public static final int ADD_INT_LIT8 = 0xd8; - public static final int RSUB_INT_LIT8 = 0xd9; - public static final int MUL_INT_LIT8 = 0xda; - public static final int DIV_INT_LIT8 = 0xdb; - public static final int REM_INT_LIT8 = 0xdc; - public static final int AND_INT_LIT8 = 0xdd; - public static final int OR_INT_LIT8 = 0xde; - public static final int XOR_INT_LIT8 = 0xdf; - public static final int SHL_INT_LIT8 = 0xe0; - public static final int SHR_INT_LIT8 = 0xe1; - public static final int USHR_INT_LIT8 = 0xe2; - // END(opcodes) + // BEGIN(opcodes); GENERATED AUTOMATICALLY BY opcode-gen + public static final int NOP = 0x00; + public static final int MOVE = 0x01; + public static final int MOVE_FROM16 = 0x02; + public static final int MOVE_16 = 0x03; + public static final int MOVE_WIDE = 0x04; + public static final int MOVE_WIDE_FROM16 = 0x05; + public static final int MOVE_WIDE_16 = 0x06; + public static final int MOVE_OBJECT = 0x07; + public static final int MOVE_OBJECT_FROM16 = 0x08; + public static final int MOVE_OBJECT_16 = 0x09; + public static final int MOVE_RESULT = 0x0a; + public static final int MOVE_RESULT_WIDE = 0x0b; + public static final int MOVE_RESULT_OBJECT = 0x0c; + public static final int MOVE_EXCEPTION = 0x0d; + public static final int RETURN_VOID = 0x0e; + public static final int RETURN = 0x0f; + public static final int RETURN_WIDE = 0x10; + public static final int RETURN_OBJECT = 0x11; + public static final int CONST_4 = 0x12; + public static final int CONST_16 = 0x13; + public static final int CONST = 0x14; + public static final int CONST_HIGH16 = 0x15; + public static final int CONST_WIDE_16 = 0x16; + public static final int CONST_WIDE_32 = 0x17; + public static final int CONST_WIDE = 0x18; + public static final int CONST_WIDE_HIGH16 = 0x19; + public static final int CONST_STRING = 0x1a; + public static final int CONST_STRING_JUMBO = 0x1b; + public static final int CONST_CLASS = 0x1c; + public static final int MONITOR_ENTER = 0x1d; + public static final int MONITOR_EXIT = 0x1e; + public static final int CHECK_CAST = 0x1f; + public static final int INSTANCE_OF = 0x20; + public static final int ARRAY_LENGTH = 0x21; + public static final int NEW_INSTANCE = 0x22; + public static final int NEW_ARRAY = 0x23; + public static final int FILLED_NEW_ARRAY = 0x24; + public static final int FILLED_NEW_ARRAY_RANGE = 0x25; + public static final int FILL_ARRAY_DATA = 0x26; + public static final int THROW = 0x27; + public static final int GOTO = 0x28; + public static final int GOTO_16 = 0x29; + public static final int GOTO_32 = 0x2a; + public static final int PACKED_SWITCH = 0x2b; + public static final int SPARSE_SWITCH = 0x2c; + public static final int CMPL_FLOAT = 0x2d; + public static final int CMPG_FLOAT = 0x2e; + public static final int CMPL_DOUBLE = 0x2f; + public static final int CMPG_DOUBLE = 0x30; + public static final int CMP_LONG = 0x31; + public static final int IF_EQ = 0x32; + public static final int IF_NE = 0x33; + public static final int IF_LT = 0x34; + public static final int IF_GE = 0x35; + public static final int IF_GT = 0x36; + public static final int IF_LE = 0x37; + public static final int IF_EQZ = 0x38; + public static final int IF_NEZ = 0x39; + public static final int IF_LTZ = 0x3a; + public static final int IF_GEZ = 0x3b; + public static final int IF_GTZ = 0x3c; + public static final int IF_LEZ = 0x3d; + public static final int AGET = 0x44; + public static final int AGET_WIDE = 0x45; + public static final int AGET_OBJECT = 0x46; + public static final int AGET_BOOLEAN = 0x47; + public static final int AGET_BYTE = 0x48; + public static final int AGET_CHAR = 0x49; + public static final int AGET_SHORT = 0x4a; + public static final int APUT = 0x4b; + public static final int APUT_WIDE = 0x4c; + public static final int APUT_OBJECT = 0x4d; + public static final int APUT_BOOLEAN = 0x4e; + public static final int APUT_BYTE = 0x4f; + public static final int APUT_CHAR = 0x50; + public static final int APUT_SHORT = 0x51; + public static final int IGET = 0x52; + public static final int IGET_WIDE = 0x53; + public static final int IGET_OBJECT = 0x54; + public static final int IGET_BOOLEAN = 0x55; + public static final int IGET_BYTE = 0x56; + public static final int IGET_CHAR = 0x57; + public static final int IGET_SHORT = 0x58; + public static final int IPUT = 0x59; + public static final int IPUT_WIDE = 0x5a; + public static final int IPUT_OBJECT = 0x5b; + public static final int IPUT_BOOLEAN = 0x5c; + public static final int IPUT_BYTE = 0x5d; + public static final int IPUT_CHAR = 0x5e; + public static final int IPUT_SHORT = 0x5f; + public static final int SGET = 0x60; + public static final int SGET_WIDE = 0x61; + public static final int SGET_OBJECT = 0x62; + public static final int SGET_BOOLEAN = 0x63; + public static final int SGET_BYTE = 0x64; + public static final int SGET_CHAR = 0x65; + public static final int SGET_SHORT = 0x66; + public static final int SPUT = 0x67; + public static final int SPUT_WIDE = 0x68; + public static final int SPUT_OBJECT = 0x69; + public static final int SPUT_BOOLEAN = 0x6a; + public static final int SPUT_BYTE = 0x6b; + public static final int SPUT_CHAR = 0x6c; + public static final int SPUT_SHORT = 0x6d; + public static final int INVOKE_VIRTUAL = 0x6e; + public static final int INVOKE_SUPER = 0x6f; + public static final int INVOKE_DIRECT = 0x70; + public static final int INVOKE_STATIC = 0x71; + public static final int INVOKE_INTERFACE = 0x72; + public static final int INVOKE_VIRTUAL_RANGE = 0x74; + public static final int INVOKE_SUPER_RANGE = 0x75; + public static final int INVOKE_DIRECT_RANGE = 0x76; + public static final int INVOKE_STATIC_RANGE = 0x77; + public static final int INVOKE_INTERFACE_RANGE = 0x78; + public static final int NEG_INT = 0x7b; + public static final int NOT_INT = 0x7c; + public static final int NEG_LONG = 0x7d; + public static final int NOT_LONG = 0x7e; + public static final int NEG_FLOAT = 0x7f; + public static final int NEG_DOUBLE = 0x80; + public static final int INT_TO_LONG = 0x81; + public static final int INT_TO_FLOAT = 0x82; + public static final int INT_TO_DOUBLE = 0x83; + public static final int LONG_TO_INT = 0x84; + public static final int LONG_TO_FLOAT = 0x85; + public static final int LONG_TO_DOUBLE = 0x86; + public static final int FLOAT_TO_INT = 0x87; + public static final int FLOAT_TO_LONG = 0x88; + public static final int FLOAT_TO_DOUBLE = 0x89; + public static final int DOUBLE_TO_INT = 0x8a; + public static final int DOUBLE_TO_LONG = 0x8b; + public static final int DOUBLE_TO_FLOAT = 0x8c; + public static final int INT_TO_BYTE = 0x8d; + public static final int INT_TO_CHAR = 0x8e; + public static final int INT_TO_SHORT = 0x8f; + public static final int ADD_INT = 0x90; + public static final int SUB_INT = 0x91; + public static final int MUL_INT = 0x92; + public static final int DIV_INT = 0x93; + public static final int REM_INT = 0x94; + public static final int AND_INT = 0x95; + public static final int OR_INT = 0x96; + public static final int XOR_INT = 0x97; + public static final int SHL_INT = 0x98; + public static final int SHR_INT = 0x99; + public static final int USHR_INT = 0x9a; + public static final int ADD_LONG = 0x9b; + public static final int SUB_LONG = 0x9c; + public static final int MUL_LONG = 0x9d; + public static final int DIV_LONG = 0x9e; + public static final int REM_LONG = 0x9f; + public static final int AND_LONG = 0xa0; + public static final int OR_LONG = 0xa1; + public static final int XOR_LONG = 0xa2; + public static final int SHL_LONG = 0xa3; + public static final int SHR_LONG = 0xa4; + public static final int USHR_LONG = 0xa5; + public static final int ADD_FLOAT = 0xa6; + public static final int SUB_FLOAT = 0xa7; + public static final int MUL_FLOAT = 0xa8; + public static final int DIV_FLOAT = 0xa9; + public static final int REM_FLOAT = 0xaa; + public static final int ADD_DOUBLE = 0xab; + public static final int SUB_DOUBLE = 0xac; + public static final int MUL_DOUBLE = 0xad; + public static final int DIV_DOUBLE = 0xae; + public static final int REM_DOUBLE = 0xaf; + public static final int ADD_INT_2ADDR = 0xb0; + public static final int SUB_INT_2ADDR = 0xb1; + public static final int MUL_INT_2ADDR = 0xb2; + public static final int DIV_INT_2ADDR = 0xb3; + public static final int REM_INT_2ADDR = 0xb4; + public static final int AND_INT_2ADDR = 0xb5; + public static final int OR_INT_2ADDR = 0xb6; + public static final int XOR_INT_2ADDR = 0xb7; + public static final int SHL_INT_2ADDR = 0xb8; + public static final int SHR_INT_2ADDR = 0xb9; + public static final int USHR_INT_2ADDR = 0xba; + public static final int ADD_LONG_2ADDR = 0xbb; + public static final int SUB_LONG_2ADDR = 0xbc; + public static final int MUL_LONG_2ADDR = 0xbd; + public static final int DIV_LONG_2ADDR = 0xbe; + public static final int REM_LONG_2ADDR = 0xbf; + public static final int AND_LONG_2ADDR = 0xc0; + public static final int OR_LONG_2ADDR = 0xc1; + public static final int XOR_LONG_2ADDR = 0xc2; + public static final int SHL_LONG_2ADDR = 0xc3; + public static final int SHR_LONG_2ADDR = 0xc4; + public static final int USHR_LONG_2ADDR = 0xc5; + public static final int ADD_FLOAT_2ADDR = 0xc6; + public static final int SUB_FLOAT_2ADDR = 0xc7; + public static final int MUL_FLOAT_2ADDR = 0xc8; + public static final int DIV_FLOAT_2ADDR = 0xc9; + public static final int REM_FLOAT_2ADDR = 0xca; + public static final int ADD_DOUBLE_2ADDR = 0xcb; + public static final int SUB_DOUBLE_2ADDR = 0xcc; + public static final int MUL_DOUBLE_2ADDR = 0xcd; + public static final int DIV_DOUBLE_2ADDR = 0xce; + public static final int REM_DOUBLE_2ADDR = 0xcf; + public static final int ADD_INT_LIT16 = 0xd0; + public static final int RSUB_INT = 0xd1; + public static final int MUL_INT_LIT16 = 0xd2; + public static final int DIV_INT_LIT16 = 0xd3; + public static final int REM_INT_LIT16 = 0xd4; + public static final int AND_INT_LIT16 = 0xd5; + public static final int OR_INT_LIT16 = 0xd6; + public static final int XOR_INT_LIT16 = 0xd7; + public static final int ADD_INT_LIT8 = 0xd8; + public static final int RSUB_INT_LIT8 = 0xd9; + public static final int MUL_INT_LIT8 = 0xda; + public static final int DIV_INT_LIT8 = 0xdb; + public static final int REM_INT_LIT8 = 0xdc; + public static final int AND_INT_LIT8 = 0xdd; + public static final int OR_INT_LIT8 = 0xde; + public static final int XOR_INT_LIT8 = 0xdf; + public static final int SHL_INT_LIT8 = 0xe0; + public static final int SHR_INT_LIT8 = 0xe1; + public static final int USHR_INT_LIT8 = 0xe2; + // END(opcodes) - // TODO: Generate these payload opcodes with opcode-gen. + // TODO(dx team): Generate these payload opcodes with opcode-gen. - /** - * special pseudo-opcode value for packed-switch data payload - * instructions - */ - public static final int PACKED_SWITCH_PAYLOAD = 0x100; + /** + * special pseudo-opcode value for packed-switch data payload + * instructions + */ + public static final int PACKED_SWITCH_PAYLOAD = 0x100; - /** special pseudo-opcode value for packed-switch data payload - * instructions - */ - public static final int SPARSE_SWITCH_PAYLOAD = 0x200; + /** special pseudo-opcode value for packed-switch data payload + * instructions + */ + public static final int SPARSE_SWITCH_PAYLOAD = 0x200; - /** special pseudo-opcode value for fill-array-data data payload - * instructions - */ - public static final int FILL_ARRAY_DATA_PAYLOAD = 0x300; + /** special pseudo-opcode value for fill-array-data data payload + * instructions + */ + public static final int FILL_ARRAY_DATA_PAYLOAD = 0x300; - /** - * This class is uninstantiable. - */ - private Opcodes() { - // This space intentionally left blank. - } + /** + * This class is uninstantiable. + */ + private Opcodes() { + // This space intentionally left blank. + } - /** - * Determines if the given opcode has the right "shape" to be - * valid. This includes the range {@code 0x01..0xfe}, the range - * {@code 0x00ff..0xffff} where the low-order byte is either - * {@code 0} or {@code 0xff}, and the special opcode values {@code - * SPECIAL_FORMAT} and {@code NO_NEXT}. Note that not all of the - * opcode values that pass this test are in fact used. This method - * is meant to perform a quick check to reject blatantly wrong - * values (e.g. when validating arguments). + /** + * Determines if the given opcode has the right "shape" to be + * valid. This includes the range {@code 0x01..0xfe}, the range + * {@code 0x00ff..0xffff} where the low-order byte is either + * {@code 0} or {@code 0xff}, and the special opcode values {@code + * SPECIAL_FORMAT} and {@code NO_NEXT}. Note that not all of the + * opcode values that pass this test are in fact used. This method + * is meant to perform a quick check to reject blatantly wrong + * values (e.g. when validating arguments). + * + * @param opcode the opcode value + * @return {@code true} iff the value has the right "shape" to be + * possibly valid + */ + public static boolean isValidShape(int opcode) { + /* + * Note: This method bakes in knowledge that all opcodes are + * one of the forms: * - * @param opcode the opcode value - * @return {@code true} iff the value has the right "shape" to be - * possibly valid + * * single byte in range 0x01..0xfe -- normal opcodes + * * (byteValue << 8) -- nop and data payload opcodes + * * ((byteValue << 8) | 0xff) -- 16-bit extended opcodes + * * SPECIAL_FORMAT or NO_NEXT -- pseudo-opcodes */ - public static boolean isValidShape(int opcode) { - /* - * Note: This method bakes in knowledge that all opcodes are - * one of the forms: - * - * * single byte in range 0x01..0xfe -- normal opcodes - * * (byteValue << 8) -- nop and data payload opcodes - * * ((byteValue << 8) | 0xff) -- 16-bit extended opcodes - * * SPECIAL_FORMAT or NO_NEXT -- pseudo-opcodes - */ - - // Note: SPECIAL_FORMAT == NO_NEXT. - if (opcode < SPECIAL_FORMAT) { - return false; - } else if (opcode == SPECIAL_FORMAT) { - return true; - } - int lowByte = opcode & 0xff; - if ((lowByte == 0) || (lowByte == 0xff)) { - return true; - } +// Note: SPECIAL_FORMAT == NO_NEXT. + if (opcode < SPECIAL_FORMAT) { + return false; + } else if (opcode == SPECIAL_FORMAT) { + return true; + } - return (opcode & 0xff00) == 0; + int lowByte = opcode & 0xff; + if ((lowByte == 0) || (lowByte == 0xff)) { + return true; } - /** - * Gets the opcode out of an opcode unit, the latter of which may also - * include one or more argument values. - * - * @param opcodeUnit the opcode-containing code unit - * @return the extracted opcode + return (opcode & 0xff00) == 0; + } + + /** + * Gets the opcode out of an opcode unit, the latter of which may also + * include one or more argument values. + * + * @param opcodeUnit the opcode-containing code unit + * @return the extracted opcode + */ + public static int extractOpcodeFromUnit(int opcodeUnit) { + /* + * Note: This method bakes in knowledge that all opcodes are + * either single-byte or of the forms (byteValue << 8) or + * ((byteValue << 8) | 0xff). */ - public static int extractOpcodeFromUnit(int opcodeUnit) { - /* - * Note: This method bakes in knowledge that all opcodes are - * either single-byte or of the forms (byteValue << 8) or - * ((byteValue << 8) | 0xff). - */ - int lowByte = opcodeUnit & 0xff; - return ((lowByte == 0) || (lowByte == 0xff)) ? opcodeUnit : lowByte; - } +int lowByte = opcodeUnit & 0xff; + return ((lowByte == 0) || (lowByte == 0xff)) ? opcodeUnit : lowByte; + } } diff --git a/dx/src/com/android/jack/dx/io/ProtoId.java b/dx/src/com/android/jack/dx/io/ProtoId.java index c1bb61a..d715720 100644 --- a/dx/src/com/android/jack/dx/io/ProtoId.java +++ b/dx/src/com/android/jack/dx/io/ProtoId.java @@ -18,51 +18,55 @@ package com.android.jack.dx.io; import com.android.jack.dx.util.Unsigned; +/** + * TODO(jack team) + */ public final class ProtoId implements Comparable<ProtoId> { - private final DexBuffer buffer; - private final int shortyIndex; - private final int returnTypeIndex; - private final int parametersOffset; - - public ProtoId(DexBuffer buffer, int shortyIndex, int returnTypeIndex, int parametersOffset) { - this.buffer = buffer; - this.shortyIndex = shortyIndex; - this.returnTypeIndex = returnTypeIndex; - this.parametersOffset = parametersOffset; - } + private final DexBuffer buffer; + private final int shortyIndex; + private final int returnTypeIndex; + private final int parametersOffset; - public int compareTo(ProtoId other) { - if (returnTypeIndex != other.returnTypeIndex) { - return Unsigned.compare(returnTypeIndex, other.returnTypeIndex); - } - return Unsigned.compare(parametersOffset, other.parametersOffset); - } + public ProtoId(DexBuffer buffer, int shortyIndex, int returnTypeIndex, int parametersOffset) { + this.buffer = buffer; + this.shortyIndex = shortyIndex; + this.returnTypeIndex = returnTypeIndex; + this.parametersOffset = parametersOffset; + } - public int getShortyIndex() { - return shortyIndex; + @Override + public int compareTo(ProtoId other) { + if (returnTypeIndex != other.returnTypeIndex) { + return Unsigned.compare(returnTypeIndex, other.returnTypeIndex); } + return Unsigned.compare(parametersOffset, other.parametersOffset); + } - public int getReturnTypeIndex() { - return returnTypeIndex; - } + public int getShortyIndex() { + return shortyIndex; + } - public int getParametersOffset() { - return parametersOffset; - } + public int getReturnTypeIndex() { + return returnTypeIndex; + } - public void writeTo(DexBuffer.Section out) { - out.writeInt(shortyIndex); - out.writeInt(returnTypeIndex); - out.writeInt(parametersOffset); - } + public int getParametersOffset() { + return parametersOffset; + } - @Override public String toString() { - if (buffer == null) { - return shortyIndex + " " + returnTypeIndex + " " + parametersOffset; - } + public void writeTo(DexBuffer.Section out) { + out.writeInt(shortyIndex); + out.writeInt(returnTypeIndex); + out.writeInt(parametersOffset); + } - return buffer.strings().get(shortyIndex) - + ": " + buffer.typeNames().get(returnTypeIndex) - + " " + buffer.readTypeList(parametersOffset); + @Override + public String toString() { + if (buffer == null) { + return shortyIndex + " " + returnTypeIndex + " " + parametersOffset; } + + return buffer.strings().get(shortyIndex) + ": " + buffer.typeNames().get(returnTypeIndex) + " " + + buffer.readTypeList(parametersOffset); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/AddressMap.java b/dx/src/com/android/jack/dx/io/instructions/AddressMap.java index 624870b..f546add 100644 --- a/dx/src/com/android/jack/dx/io/instructions/AddressMap.java +++ b/dx/src/com/android/jack/dx/io/instructions/AddressMap.java @@ -16,7 +16,6 @@ package com.android.jack.dx.io.instructions; -import java.io.EOFException; import java.util.HashMap; /** @@ -24,29 +23,29 @@ import java.util.HashMap; * {@code int}s. */ public final class AddressMap { - /** underlying map. TODO: This might be too inefficient. */ - private final HashMap<Integer,Integer> map; + /** underlying map. TODO(dx team): This might be too inefficient. */ + private final HashMap<Integer, Integer> map; - /** - * Constructs an instance. - */ - public AddressMap() { - map = new HashMap<Integer,Integer>(); - } + /** + * Constructs an instance. + */ + public AddressMap() { + map = new HashMap<Integer, Integer>(); + } - /** - * Gets the value address corresponding to the given key address. Returns - * {@code -1} if there is no mapping. - */ - public int get(int keyAddress) { - Integer value = map.get(keyAddress); - return (value == null) ? -1 : value; - } + /** + * Gets the value address corresponding to the given key address. Returns + * {@code -1} if there is no mapping. + */ + public int get(int keyAddress) { + Integer value = map.get(keyAddress); + return (value == null) ? -1 : value; + } - /** - * Sets the value address associated with the given key address. - */ - public void put(int keyAddress, int valueAddress) { - map.put(keyAddress, valueAddress); - } + /** + * Sets the value address associated with the given key address. + */ + public void put(int keyAddress, int valueAddress) { + map.put(keyAddress, valueAddress); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/BaseCodeCursor.java b/dx/src/com/android/jack/dx/io/instructions/BaseCodeCursor.java index 8d82cae..f62c9aa 100644 --- a/dx/src/com/android/jack/dx/io/instructions/BaseCodeCursor.java +++ b/dx/src/com/android/jack/dx/io/instructions/BaseCodeCursor.java @@ -16,46 +16,48 @@ package com.android.jack.dx.io.instructions; -import java.io.EOFException; /** * Base implementation of {@link CodeCursor}. */ public abstract class BaseCodeCursor implements CodeCursor { - /** base address map */ - private final AddressMap baseAddressMap; - - /** next index within {@link #array} to read from or write to */ - private int cursor; - - /** - * Constructs an instance. - */ - public BaseCodeCursor() { - this.baseAddressMap = new AddressMap(); - this.cursor = 0; - } - - /** @inheritDoc */ - public final int cursor() { - return cursor; - } - - /** @inheritDoc */ - public final int baseAddressForCursor() { - int mapped = baseAddressMap.get(cursor); - return (mapped >= 0) ? mapped : cursor; - } - - /** @inheritDoc */ - public final void setBaseAddress(int targetAddress, int baseAddress) { - baseAddressMap.put(targetAddress, baseAddress); - } - - /** - * Advance the cursor by the indicated amount. - */ - protected final void advance(int amount) { - cursor += amount; - } + /** base address map */ + private final AddressMap baseAddressMap; + + /** next index within {@link #array} to read from or write to */ + private int cursor; + + /** + * Constructs an instance. + */ + public BaseCodeCursor() { + this.baseAddressMap = new AddressMap(); + this.cursor = 0; + } + + /** @inheritDoc */ + @Override + public final int cursor() { + return cursor; + } + + /** @inheritDoc */ + @Override + public final int baseAddressForCursor() { + int mapped = baseAddressMap.get(cursor); + return (mapped >= 0) ? mapped : cursor; + } + + /** @inheritDoc */ + @Override + public final void setBaseAddress(int targetAddress, int baseAddress) { + baseAddressMap.put(targetAddress, baseAddress); + } + + /** + * Advance the cursor by the indicated amount. + */ + protected final void advance(int amount) { + cursor += amount; + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/CodeCursor.java b/dx/src/com/android/jack/dx/io/instructions/CodeCursor.java index b9dbbbe..98e8eae 100644 --- a/dx/src/com/android/jack/dx/io/instructions/CodeCursor.java +++ b/dx/src/com/android/jack/dx/io/instructions/CodeCursor.java @@ -20,28 +20,28 @@ package com.android.jack.dx.io.instructions; * Cursor over code units, for reading or writing out Dalvik bytecode. */ public interface CodeCursor { - /** - * Gets the cursor. The cursor is the offset in code units from - * the start of the input of the next code unit to be read or - * written, where the input generally consists of the code for a - * single method. - */ - public int cursor(); + /** + * Gets the cursor. The cursor is the offset in code units from + * the start of the input of the next code unit to be read or + * written, where the input generally consists of the code for a + * single method. + */ + public int cursor(); - /** - * Gets the base address associated with the current cursor. This - * differs from the cursor value when explicitly set (by {@link - * #setBaseAddress). This is used, in particular, to convey base - * addresses to switch data payload instructions, whose relative - * addresses are relative to the address of a dependant switch - * instruction. - */ - public int baseAddressForCursor(); + /** + * Gets the base address associated with the current cursor. This + * differs from the cursor value when explicitly set (by {@link + * #setBaseAddress). This is used, in particular, to convey base + * addresses to switch data payload instructions, whose relative + * addresses are relative to the address of a dependant switch + * instruction. + */ + public int baseAddressForCursor(); - /** - * Sets the base address for the given target address to be as indicated. - * - * @see #baseAddressForCursor - */ - public void setBaseAddress(int targetAddress, int baseAddress); + /** + * Sets the base address for the given target address to be as indicated. + * + * @see #baseAddressForCursor + */ + public void setBaseAddress(int targetAddress, int baseAddress); } diff --git a/dx/src/com/android/jack/dx/io/instructions/CodeInput.java b/dx/src/com/android/jack/dx/io/instructions/CodeInput.java index 9327dc4..cec6a48 100644 --- a/dx/src/com/android/jack/dx/io/instructions/CodeInput.java +++ b/dx/src/com/android/jack/dx/io/instructions/CodeInput.java @@ -22,24 +22,24 @@ import java.io.EOFException; * Input stream of code units, for reading in Dalvik bytecode. */ public interface CodeInput extends CodeCursor { - /** - * Returns whether there are any more code units to read. This - * is analogous to {@code hasNext()} on an interator. - */ - public boolean hasMore(); + /** + * Returns whether there are any more code units to read. This + * is analogous to {@code hasNext()} on an interator. + */ + public boolean hasMore(); - /** - * Reads a code unit. - */ - public int read() throws EOFException; + /** + * Reads a code unit. + */ + public int read() throws EOFException; - /** - * Reads two code units, treating them as a little-endian {@code int}. - */ - public int readInt() throws EOFException; + /** + * Reads two code units, treating them as a little-endian {@code int}. + */ + public int readInt() throws EOFException; - /** - * Reads four code units, treating them as a little-endian {@code long}. - */ - public long readLong() throws EOFException; + /** + * Reads four code units, treating them as a little-endian {@code long}. + */ + public long readLong() throws EOFException; } diff --git a/dx/src/com/android/jack/dx/io/instructions/CodeOutput.java b/dx/src/com/android/jack/dx/io/instructions/CodeOutput.java index 76f4d14..3de07ac 100644 --- a/dx/src/com/android/jack/dx/io/instructions/CodeOutput.java +++ b/dx/src/com/android/jack/dx/io/instructions/CodeOutput.java @@ -20,58 +20,58 @@ package com.android.jack.dx.io.instructions; * Output stream of code units, for writing out Dalvik bytecode. */ public interface CodeOutput extends CodeCursor { - /** - * Writes a code unit. - */ - public void write(short codeUnit); + /** + * Writes a code unit. + */ + public void write(short codeUnit); - /** - * Writes two code units. - */ - public void write(short u0, short u1); + /** + * Writes two code units. + */ + public void write(short u0, short u1); - /** - * Writes three code units. - */ - public void write(short u0, short u1, short u2); + /** + * Writes three code units. + */ + public void write(short u0, short u1, short u2); - /** - * Writes four code units. - */ - public void write(short u0, short u1, short u2, short u3); + /** + * Writes four code units. + */ + public void write(short u0, short u1, short u2, short u3); - /** - * Writes five code units. - */ - public void write(short u0, short u1, short u2, short u3, short u4); + /** + * Writes five code units. + */ + public void write(short u0, short u1, short u2, short u3, short u4); - /** - * Writes an {@code int}, little-endian. - */ - public void writeInt(int value); + /** + * Writes an {@code int}, little-endian. + */ + public void writeInt(int value); - /** - * Writes a {@code long}, little-endian. - */ - public void writeLong(long value); + /** + * Writes a {@code long}, little-endian. + */ + public void writeLong(long value); - /** - * Writes the contents of the given array. - */ - public void write(byte[] data); + /** + * Writes the contents of the given array. + */ + public void write(byte[] data); - /** - * Writes the contents of the given array. - */ - public void write(short[] data); + /** + * Writes the contents of the given array. + */ + public void write(short[] data); - /** - * Writes the contents of the given array. - */ - public void write(int[] data); + /** + * Writes the contents of the given array. + */ + public void write(int[] data); - /** - * Writes the contents of the given array. - */ - public void write(long[] data); + /** + * Writes the contents of the given array. + */ + public void write(long[] data); } diff --git a/dx/src/com/android/jack/dx/io/instructions/DecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/DecodedInstruction.java index efcdec6..0d71a91 100644 --- a/dx/src/com/android/jack/dx/io/instructions/DecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/DecodedInstruction.java @@ -38,442 +38,443 @@ import java.io.EOFException; * consistently named alphabetically.</p> */ public abstract class DecodedInstruction { - /** non-null; instruction format / codec */ - private final InstructionCodec format; - - /** opcode number */ - private final int opcode; - - /** constant index argument */ - private final int index; - - /** null-ok; index type */ - private final IndexType indexType; - - /** - * target address argument. This is an absolute address, not just - * a signed offset. <b>Note:</b> The address is unsigned, even - * though it is stored in an {@code int}. - */ - private final int target; - - /** - * literal value argument; also used for special verification error - * constants (format 20bc) as well as should-be-zero values - * (formats 10x, 20t, 30t, and 32x) - */ - private final long literal; - - /** - * Decodes an instruction from the given input source. - */ - public static DecodedInstruction decode(CodeInput in) throws EOFException { - int opcodeUnit = in.read(); - int opcode = Opcodes.extractOpcodeFromUnit(opcodeUnit); - InstructionCodec format = OpcodeInfo.getFormat(opcode); - - return format.decode(opcodeUnit, in); + /** non-null; instruction format / codec */ + private final InstructionCodec format; + + /** opcode number */ + private final int opcode; + + /** constant index argument */ + private final int index; + + /** null-ok; index type */ + private final IndexType indexType; + + /** + * target address argument. This is an absolute address, not just + * a signed offset. <b>Note:</b> The address is unsigned, even + * though it is stored in an {@code int}. + */ + private final int target; + + /** + * literal value argument; also used for special verification error + * constants (format 20bc) as well as should-be-zero values + * (formats 10x, 20t, 30t, and 32x) + */ + private final long literal; + + /** + * Decodes an instruction from the given input source. + */ + public static DecodedInstruction decode(CodeInput in) throws EOFException { + int opcodeUnit = in.read(); + int opcode = Opcodes.extractOpcodeFromUnit(opcodeUnit); + InstructionCodec format = OpcodeInfo.getFormat(opcode); + + return format.decode(opcodeUnit, in); + } + + /** + * Decodes an array of instructions. The result has non-null + * elements at each offset that represents the start of an + * instruction. + */ + public static DecodedInstruction[] decodeAll(short[] encodedInstructions) { + int size = encodedInstructions.length; + DecodedInstruction[] decoded = new DecodedInstruction[size]; + ShortArrayCodeInput in = new ShortArrayCodeInput(encodedInstructions); + + try { + while (in.hasMore()) { + decoded[in.cursor()] = DecodedInstruction.decode(in); + } + } catch (EOFException ex) { + throw new DexException(ex); + } + + return decoded; + } + + /** + * Constructs an instance. + */ + public DecodedInstruction(InstructionCodec format, + int opcode, + int index, + IndexType indexType, + int target, + long literal) { + if (format == null) { + throw new NullPointerException("format == null"); + } + + if (!Opcodes.isValidShape(opcode)) { + throw new IllegalArgumentException("invalid opcode"); + } + + this.format = format; + this.opcode = opcode; + this.index = index; + this.indexType = indexType; + this.target = target; + this.literal = literal; + } + + public final InstructionCodec getFormat() { + return format; + } + + public final int getOpcode() { + return opcode; + } + + /** + * Gets the opcode, as a code unit. + */ + public final short getOpcodeUnit() { + return (short) opcode; + } + + public final int getIndex() { + return index; + } + + /** + * Gets the index, as a code unit. + */ + public final short getIndexUnit() { + return (short) index; + } + + public final IndexType getIndexType() { + return indexType; + } + + /** + * Gets the raw target. + */ + public final int getTarget() { + return target; + } + + /** + * Gets the target as a relative offset from the given address. + */ + public final int getTarget(int baseAddress) { + return target - baseAddress; + } + + /** + * Gets the target as a relative offset from the given base + * address, as a code unit. This will throw if the value is out of + * the range of a signed code unit. + */ + public final short getTargetUnit(int baseAddress) { + int relativeTarget = getTarget(baseAddress); + + if (relativeTarget != (short) relativeTarget) { + throw new DexException("Target out of range: " + Hex.s4(relativeTarget)); + } + + return (short) relativeTarget; + } + + /** + * Gets the target as a relative offset from the given base + * address, masked to be a byte in size. This will throw if the + * value is out of the range of a signed byte. + */ + public final int getTargetByte(int baseAddress) { + int relativeTarget = getTarget(baseAddress); + + if (relativeTarget != (byte) relativeTarget) { + throw new DexException("Target out of range: " + Hex.s4(relativeTarget)); + } + + return relativeTarget & 0xff; + } + + public final long getLiteral() { + return literal; + } + + /** + * Gets the literal value, masked to be an int in size. This will + * throw if the value is out of the range of a signed int. + */ + public final int getLiteralInt() { + if (literal != (int) literal) { + throw new DexException("Literal out of range: " + Hex.u8(literal)); } - /** - * Decodes an array of instructions. The result has non-null - * elements at each offset that represents the start of an - * instruction. - */ - public static DecodedInstruction[] decodeAll(short[] encodedInstructions) { - int size = encodedInstructions.length; - DecodedInstruction[] decoded = new DecodedInstruction[size]; - ShortArrayCodeInput in = new ShortArrayCodeInput(encodedInstructions); - - try { - while (in.hasMore()) { - decoded[in.cursor()] = DecodedInstruction.decode(in); - } - } catch (EOFException ex) { - throw new DexException(ex); - } - - return decoded; - } + return (int) literal; + } - /** - * Constructs an instance. - */ - public DecodedInstruction(InstructionCodec format, int opcode, - int index, IndexType indexType, int target, long literal) { - if (format == null) { - throw new NullPointerException("format == null"); - } - - if (!Opcodes.isValidShape(opcode)) { - throw new IllegalArgumentException("invalid opcode"); - } - - this.format = format; - this.opcode = opcode; - this.index = index; - this.indexType = indexType; - this.target = target; - this.literal = literal; - } + /** + * Gets the literal value, as a code unit. This will throw if the + * value is out of the range of a signed code unit. + */ + public final short getLiteralUnit() { + if (literal != (short) literal) { + throw new DexException("Literal out of range: " + Hex.u8(literal)); + } + + return (short) literal; + } - public final InstructionCodec getFormat() { - return format; - } + /** + * Gets the literal value, masked to be a byte in size. This will + * throw if the value is out of the range of a signed byte. + */ + public final int getLiteralByte() { + if (literal != (byte) literal) { + throw new DexException("Literal out of range: " + Hex.u8(literal)); + } - public final int getOpcode() { - return opcode; - } + return (int) literal & 0xff; + } - /** - * Gets the opcode, as a code unit. - */ - public final short getOpcodeUnit() { - return (short) opcode; + /** + * Gets the literal value, masked to be a nibble in size. This + * will throw if the value is out of the range of a signed nibble. + */ + public final int getLiteralNibble() { + if ((literal < -8) || (literal > 7)) { + throw new DexException("Literal out of range: " + Hex.u8(literal)); } - public final int getIndex() { - return index; - } + return (int) literal & 0xf; + } - /** - * Gets the index, as a code unit. - */ - public final short getIndexUnit() { - return (short) index; - } + public abstract int getRegisterCount(); - public final IndexType getIndexType() { - return indexType; - } + public int getA() { + return 0; + } - /** - * Gets the raw target. - */ - public final int getTarget() { - return target; - } + public int getB() { + return 0; + } - /** - * Gets the target as a relative offset from the given address. - */ - public final int getTarget(int baseAddress) { - return target - baseAddress; - } + public int getC() { + return 0; + } - /** - * Gets the target as a relative offset from the given base - * address, as a code unit. This will throw if the value is out of - * the range of a signed code unit. - */ - public final short getTargetUnit(int baseAddress) { - int relativeTarget = getTarget(baseAddress); + public int getD() { + return 0; + } - if (relativeTarget != (short) relativeTarget) { - throw new DexException("Target out of range: " - + Hex.s4(relativeTarget)); - } - - return (short) relativeTarget; - } + public int getE() { + return 0; + } - /** - * Gets the target as a relative offset from the given base - * address, masked to be a byte in size. This will throw if the - * value is out of the range of a signed byte. - */ - public final int getTargetByte(int baseAddress) { - int relativeTarget = getTarget(baseAddress); + /** + * Gets the register count, as a code unit. This will throw if the + * value is out of the range of an unsigned code unit. + */ + public final short getRegisterCountUnit() { + int registerCount = getRegisterCount(); - if (relativeTarget != (byte) relativeTarget) { - throw new DexException("Target out of range: " - + Hex.s4(relativeTarget)); - } - - return relativeTarget & 0xff; - } - - public final long getLiteral() { - return literal; + if ((registerCount & ~0xffff) != 0) { + throw new DexException("Register count out of range: " + Hex.u8(registerCount)); } - /** - * Gets the literal value, masked to be an int in size. This will - * throw if the value is out of the range of a signed int. - */ - public final int getLiteralInt() { - if (literal != (int) literal) { - throw new DexException("Literal out of range: " + Hex.u8(literal)); - } - - return (int) literal; - } + return (short) registerCount; + } - /** - * Gets the literal value, as a code unit. This will throw if the - * value is out of the range of a signed code unit. - */ - public final short getLiteralUnit() { - if (literal != (short) literal) { - throw new DexException("Literal out of range: " + Hex.u8(literal)); - } + /** + * Gets the A register number, as a code unit. This will throw if the + * value is out of the range of an unsigned code unit. + */ + public final short getAUnit() { + int a = getA(); - return (short) literal; + if ((a & ~0xffff) != 0) { + throw new DexException("Register A out of range: " + Hex.u8(a)); } - /** - * Gets the literal value, masked to be a byte in size. This will - * throw if the value is out of the range of a signed byte. - */ - public final int getLiteralByte() { - if (literal != (byte) literal) { - throw new DexException("Literal out of range: " + Hex.u8(literal)); - } + return (short) a; + } - return (int) literal & 0xff; - } - - /** - * Gets the literal value, masked to be a nibble in size. This - * will throw if the value is out of the range of a signed nibble. - */ - public final int getLiteralNibble() { - if ((literal < -8) || (literal > 7)) { - throw new DexException("Literal out of range: " + Hex.u8(literal)); - } + /** + * Gets the A register number, as a byte. This will throw if the + * value is out of the range of an unsigned byte. + */ + public final short getAByte() { + int a = getA(); - return (int) literal & 0xf; + if ((a & ~0xff) != 0) { + throw new DexException("Register A out of range: " + Hex.u8(a)); } - public abstract int getRegisterCount(); + return (short) a; + } - public int getA() { - return 0; - } + /** + * Gets the A register number, as a nibble. This will throw if the + * value is out of the range of an unsigned nibble. + */ + public final short getANibble() { + int a = getA(); - public int getB() { - return 0; + if ((a & ~0xf) != 0) { + throw new DexException("Register A out of range: " + Hex.u8(a)); } - public int getC() { - return 0; - } + return (short) a; + } - public int getD() { - return 0; - } + /** + * Gets the B register number, as a code unit. This will throw if the + * value is out of the range of an unsigned code unit. + */ + public final short getBUnit() { + int b = getB(); - public int getE() { - return 0; + if ((b & ~0xffff) != 0) { + throw new DexException("Register B out of range: " + Hex.u8(b)); } - /** - * Gets the register count, as a code unit. This will throw if the - * value is out of the range of an unsigned code unit. - */ - public final short getRegisterCountUnit() { - int registerCount = getRegisterCount(); + return (short) b; + } - if ((registerCount & ~0xffff) != 0) { - throw new DexException("Register count out of range: " - + Hex.u8(registerCount)); - } + /** + * Gets the B register number, as a byte. This will throw if the + * value is out of the range of an unsigned byte. + */ + public final short getBByte() { + int b = getB(); - return (short) registerCount; + if ((b & ~0xff) != 0) { + throw new DexException("Register B out of range: " + Hex.u8(b)); } - /** - * Gets the A register number, as a code unit. This will throw if the - * value is out of the range of an unsigned code unit. - */ - public final short getAUnit() { - int a = getA(); + return (short) b; + } - if ((a & ~0xffff) != 0) { - throw new DexException("Register A out of range: " + Hex.u8(a)); - } + /** + * Gets the B register number, as a nibble. This will throw if the + * value is out of the range of an unsigned nibble. + */ + public final short getBNibble() { + int b = getB(); - return (short) a; + if ((b & ~0xf) != 0) { + throw new DexException("Register B out of range: " + Hex.u8(b)); } - /** - * Gets the A register number, as a byte. This will throw if the - * value is out of the range of an unsigned byte. - */ - public final short getAByte() { - int a = getA(); + return (short) b; + } - if ((a & ~0xff) != 0) { - throw new DexException("Register A out of range: " + Hex.u8(a)); - } + /** + * Gets the C register number, as a code unit. This will throw if the + * value is out of the range of an unsigned code unit. + */ + public final short getCUnit() { + int c = getC(); - return (short) a; + if ((c & ~0xffff) != 0) { + throw new DexException("Register C out of range: " + Hex.u8(c)); } - /** - * Gets the A register number, as a nibble. This will throw if the - * value is out of the range of an unsigned nibble. - */ - public final short getANibble() { - int a = getA(); + return (short) c; + } - if ((a & ~0xf) != 0) { - throw new DexException("Register A out of range: " + Hex.u8(a)); - } + /** + * Gets the C register number, as a byte. This will throw if the + * value is out of the range of an unsigned byte. + */ + public final short getCByte() { + int c = getC(); - return (short) a; + if ((c & ~0xff) != 0) { + throw new DexException("Register C out of range: " + Hex.u8(c)); } - /** - * Gets the B register number, as a code unit. This will throw if the - * value is out of the range of an unsigned code unit. - */ - public final short getBUnit() { - int b = getB(); + return (short) c; + } - if ((b & ~0xffff) != 0) { - throw new DexException("Register B out of range: " + Hex.u8(b)); - } + /** + * Gets the C register number, as a nibble. This will throw if the + * value is out of the range of an unsigned nibble. + */ + public final short getCNibble() { + int c = getC(); - return (short) b; + if ((c & ~0xf) != 0) { + throw new DexException("Register C out of range: " + Hex.u8(c)); } - /** - * Gets the B register number, as a byte. This will throw if the - * value is out of the range of an unsigned byte. - */ - public final short getBByte() { - int b = getB(); + return (short) c; + } - if ((b & ~0xff) != 0) { - throw new DexException("Register B out of range: " + Hex.u8(b)); - } + /** + * Gets the D register number, as a code unit. This will throw if the + * value is out of the range of an unsigned code unit. + */ + public final short getDUnit() { + int d = getD(); - return (short) b; + if ((d & ~0xffff) != 0) { + throw new DexException("Register D out of range: " + Hex.u8(d)); } - /** - * Gets the B register number, as a nibble. This will throw if the - * value is out of the range of an unsigned nibble. - */ - public final short getBNibble() { - int b = getB(); + return (short) d; + } - if ((b & ~0xf) != 0) { - throw new DexException("Register B out of range: " + Hex.u8(b)); - } + /** + * Gets the D register number, as a byte. This will throw if the + * value is out of the range of an unsigned byte. + */ + public final short getDByte() { + int d = getD(); - return (short) b; + if ((d & ~0xff) != 0) { + throw new DexException("Register D out of range: " + Hex.u8(d)); } - /** - * Gets the C register number, as a code unit. This will throw if the - * value is out of the range of an unsigned code unit. - */ - public final short getCUnit() { - int c = getC(); + return (short) d; + } - if ((c & ~0xffff) != 0) { - throw new DexException("Register C out of range: " + Hex.u8(c)); - } + /** + * Gets the D register number, as a nibble. This will throw if the + * value is out of the range of an unsigned nibble. + */ + public final short getDNibble() { + int d = getD(); - return (short) c; + if ((d & ~0xf) != 0) { + throw new DexException("Register D out of range: " + Hex.u8(d)); } - /** - * Gets the C register number, as a byte. This will throw if the - * value is out of the range of an unsigned byte. - */ - public final short getCByte() { - int c = getC(); + return (short) d; + } - if ((c & ~0xff) != 0) { - throw new DexException("Register C out of range: " + Hex.u8(c)); - } + /** + * Gets the E register number, as a nibble. This will throw if the + * value is out of the range of an unsigned nibble. + */ + public final short getENibble() { + int e = getE(); - return (short) c; + if ((e & ~0xf) != 0) { + throw new DexException("Register E out of range: " + Hex.u8(e)); } - /** - * Gets the C register number, as a nibble. This will throw if the - * value is out of the range of an unsigned nibble. - */ - public final short getCNibble() { - int c = getC(); + return (short) e; + } - if ((c & ~0xf) != 0) { - throw new DexException("Register C out of range: " + Hex.u8(c)); - } - - return (short) c; - } - - /** - * Gets the D register number, as a code unit. This will throw if the - * value is out of the range of an unsigned code unit. - */ - public final short getDUnit() { - int d = getD(); - - if ((d & ~0xffff) != 0) { - throw new DexException("Register D out of range: " + Hex.u8(d)); - } - - return (short) d; - } - - /** - * Gets the D register number, as a byte. This will throw if the - * value is out of the range of an unsigned byte. - */ - public final short getDByte() { - int d = getD(); - - if ((d & ~0xff) != 0) { - throw new DexException("Register D out of range: " + Hex.u8(d)); - } - - return (short) d; - } - - /** - * Gets the D register number, as a nibble. This will throw if the - * value is out of the range of an unsigned nibble. - */ - public final short getDNibble() { - int d = getD(); - - if ((d & ~0xf) != 0) { - throw new DexException("Register D out of range: " + Hex.u8(d)); - } - - return (short) d; - } - - /** - * Gets the E register number, as a nibble. This will throw if the - * value is out of the range of an unsigned nibble. - */ - public final short getENibble() { - int e = getE(); - - if ((e & ~0xf) != 0) { - throw new DexException("Register E out of range: " + Hex.u8(e)); - } - - return (short) e; - } - - /** - * Encodes this instance to the given output. - */ - public final void encode(CodeOutput out) { - format.encode(this, out); - } + /** + * Encodes this instance to the given output. + */ + public final void encode(CodeOutput out) { + format.encode(this, out); + } - /** - * Returns an instance just like this one, except with the index replaced - * with the given one. - */ - public abstract DecodedInstruction withIndex(int newIndex); + /** + * Returns an instance just like this one, except with the index replaced + * with the given one. + */ + public abstract DecodedInstruction withIndex(int newIndex); } diff --git a/dx/src/com/android/jack/dx/io/instructions/FillArrayDataPayloadDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/FillArrayDataPayloadDecodedInstruction.java index 4e59342..35ada3e 100644 --- a/dx/src/com/android/jack/dx/io/instructions/FillArrayDataPayloadDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/FillArrayDataPayloadDecodedInstruction.java @@ -20,81 +20,78 @@ package com.android.jack.dx.io.instructions; * A decoded Dalvik instruction which contains the payload for * a {@code packed-switch} instruction. */ -public final class FillArrayDataPayloadDecodedInstruction - extends DecodedInstruction { - /** data array */ - private final Object data; +public final class FillArrayDataPayloadDecodedInstruction extends DecodedInstruction { + /** data array */ + private final Object data; - /** number of elements */ - private final int size; + /** number of elements */ + private final int size; - /** element width */ - private final int elementWidth; + /** element width */ + private final int elementWidth; - /** - * Constructs an instance. This private instance doesn't check the - * type of the data array. - */ - private FillArrayDataPayloadDecodedInstruction(InstructionCodec format, - int opcode, Object data, int size, int elementWidth) { - super(format, opcode, 0, null, 0, 0L); + /** + * Constructs an instance. This private instance doesn't check the + * type of the data array. + */ + private FillArrayDataPayloadDecodedInstruction(InstructionCodec format, int opcode, Object data, + int size, int elementWidth) { + super(format, opcode, 0, null, 0, 0L); - this.data = data; - this.size = size; - this.elementWidth = elementWidth; - } + this.data = data; + this.size = size; + this.elementWidth = elementWidth; + } - /** - * Constructs an instance. - */ - public FillArrayDataPayloadDecodedInstruction(InstructionCodec format, - int opcode, byte[] data) { - this(format, opcode, data, data.length, 1); - } + /** + * Constructs an instance. + */ + public FillArrayDataPayloadDecodedInstruction(InstructionCodec format, int opcode, byte[] data) { + this(format, opcode, data, data.length, 1); + } - /** - * Constructs an instance. - */ - public FillArrayDataPayloadDecodedInstruction(InstructionCodec format, - int opcode, short[] data) { - this(format, opcode, data, data.length, 2); - } + /** + * Constructs an instance. + */ + public FillArrayDataPayloadDecodedInstruction(InstructionCodec format, int opcode, short[] data) { + this(format, opcode, data, data.length, 2); + } - /** - * Constructs an instance. - */ - public FillArrayDataPayloadDecodedInstruction(InstructionCodec format, - int opcode, int[] data) { - this(format, opcode, data, data.length, 4); - } + /** + * Constructs an instance. + */ + public FillArrayDataPayloadDecodedInstruction(InstructionCodec format, int opcode, int[] data) { + this(format, opcode, data, data.length, 4); + } - /** - * Constructs an instance. - */ - public FillArrayDataPayloadDecodedInstruction(InstructionCodec format, - int opcode, long[] data) { - this(format, opcode, data, data.length, 8); - } + /** + * Constructs an instance. + */ + public FillArrayDataPayloadDecodedInstruction(InstructionCodec format, int opcode, long[] data) { + this(format, opcode, data, data.length, 8); + } - /** @inheritDoc */ - public int getRegisterCount() { - return 0; - } + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 0; + } - public short getElementWidthUnit() { - return (short) elementWidth; - } + public short getElementWidthUnit() { + return (short) elementWidth; + } - public int getSize() { - return size; - } + public int getSize() { + return size; + } - public Object getData() { - return data; - } + public Object getData() { + return data; + } - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - throw new UnsupportedOperationException("no index in instruction"); - } + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + throw new UnsupportedOperationException("no index in instruction"); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/FiveRegisterDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/FiveRegisterDecodedInstruction.java index 0ed17b3..a7a1aea 100644 --- a/dx/src/com/android/jack/dx/io/instructions/FiveRegisterDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/FiveRegisterDecodedInstruction.java @@ -22,70 +22,93 @@ import com.android.jack.dx.io.IndexType; * A decoded Dalvik instruction which has five register arguments. */ public final class FiveRegisterDecodedInstruction extends DecodedInstruction { - /** register argument "A" */ - private final int a; - - /** register argument "B" */ - private final int b; - - /** register argument "C" */ - private final int c; - - /** register argument "D" */ - private final int d; - - /** register argument "E" */ - private final int e; - - /** - * Constructs an instance. - */ - public FiveRegisterDecodedInstruction(InstructionCodec format, int opcode, - int index, IndexType indexType, int target, long literal, - int a, int b, int c, int d, int e) { - super(format, opcode, index, indexType, target, literal); - - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.e = e; - } - - /** @inheritDoc */ - public int getRegisterCount() { - return 5; - } - - /** @inheritDoc */ - public int getA() { - return a; - } - - /** @inheritDoc */ - public int getB() { - return b; - } - - /** @inheritDoc */ - public int getC() { - return c; - } - - /** @inheritDoc */ - public int getD() { - return d; - } - - /** @inheritDoc */ - public int getE() { - return e; - } - - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - return new FiveRegisterDecodedInstruction( - getFormat(), getOpcode(), newIndex, getIndexType(), - getTarget(), getLiteral(), a, b, c, d, e); - } + /** register argument "A" */ + private final int a; + + /** register argument "B" */ + private final int b; + + /** register argument "C" */ + private final int c; + + /** register argument "D" */ + private final int d; + + /** register argument "E" */ + private final int e; + + /** + * Constructs an instance. + */ + public FiveRegisterDecodedInstruction(InstructionCodec format, + int opcode, + int index, + IndexType indexType, + int target, + long literal, + int a, + int b, + int c, + int d, + int e) { + super(format, opcode, index, indexType, target, literal); + + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + } + + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 5; + } + + /** @inheritDoc */ + @Override + public int getA() { + return a; + } + + /** @inheritDoc */ + @Override + public int getB() { + return b; + } + + /** @inheritDoc */ + @Override + public int getC() { + return c; + } + + /** @inheritDoc */ + @Override + public int getD() { + return d; + } + + /** @inheritDoc */ + @Override + public int getE() { + return e; + } + + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + return new FiveRegisterDecodedInstruction(getFormat(), + getOpcode(), + newIndex, + getIndexType(), + getTarget(), + getLiteral(), + a, + b, + c, + d, + e); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/FourRegisterDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/FourRegisterDecodedInstruction.java index 9d3e7e3..912a8eb 100644 --- a/dx/src/com/android/jack/dx/io/instructions/FourRegisterDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/FourRegisterDecodedInstruction.java @@ -22,61 +22,81 @@ import com.android.jack.dx.io.IndexType; * A decoded Dalvik instruction which has five register arguments. */ public final class FourRegisterDecodedInstruction extends DecodedInstruction { - /** register argument "A" */ - private final int a; + /** register argument "A" */ + private final int a; - /** register argument "B" */ - private final int b; + /** register argument "B" */ + private final int b; - /** register argument "C" */ - private final int c; + /** register argument "C" */ + private final int c; - /** register argument "D" */ - private final int d; + /** register argument "D" */ + private final int d; - /** - * Constructs an instance. - */ - public FourRegisterDecodedInstruction(InstructionCodec format, int opcode, - int index, IndexType indexType, int target, long literal, - int a, int b, int c, int d) { - super(format, opcode, index, indexType, target, literal); + /** + * Constructs an instance. + */ + public FourRegisterDecodedInstruction(InstructionCodec format, + int opcode, + int index, + IndexType indexType, + int target, + long literal, + int a, + int b, + int c, + int d) { + super(format, opcode, index, indexType, target, literal); - this.a = a; - this.b = b; - this.c = c; - this.d = d; - } + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } - /** @inheritDoc */ - public int getRegisterCount() { - return 4; - } + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 4; + } - /** @inheritDoc */ - public int getA() { - return a; - } + /** @inheritDoc */ + @Override + public int getA() { + return a; + } - /** @inheritDoc */ - public int getB() { - return b; - } + /** @inheritDoc */ + @Override + public int getB() { + return b; + } - /** @inheritDoc */ - public int getC() { - return c; - } + /** @inheritDoc */ + @Override + public int getC() { + return c; + } - /** @inheritDoc */ - public int getD() { - return d; - } + /** @inheritDoc */ + @Override + public int getD() { + return d; + } - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - return new FourRegisterDecodedInstruction( - getFormat(), getOpcode(), newIndex, getIndexType(), - getTarget(), getLiteral(), a, b, c, d); - } + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + return new FourRegisterDecodedInstruction(getFormat(), + getOpcode(), + newIndex, + getIndexType(), + getTarget(), + getLiteral(), + a, + b, + c, + d); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/InstructionCodec.java b/dx/src/com/android/jack/dx/io/instructions/InstructionCodec.java index d24d930..c913fcb 100644 --- a/dx/src/com/android/jack/dx/io/instructions/InstructionCodec.java +++ b/dx/src/com/android/jack/dx/io/instructions/InstructionCodec.java @@ -29,934 +29,864 @@ import java.io.EOFException; * and encode from instances of {@link DecodedInstruction}. */ public enum InstructionCodec { - FORMAT_00X() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - return new ZeroRegisterDecodedInstruction( - this, opcodeUnit, 0, null, - 0, 0L); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write(insn.getOpcodeUnit()); - } - }, - - FORMAT_10X() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int literal = byte1(opcodeUnit); // should be zero - return new ZeroRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write(insn.getOpcodeUnit()); - } - }, - - FORMAT_12X() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = nibble2(opcodeUnit); - int b = nibble3(opcodeUnit); - return new TwoRegisterDecodedInstruction( - this, opcode, 0, null, - 0, 0L, - a, b); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcodeUnit(), - makeByte(insn.getA(), insn.getB()))); - } - }, - - FORMAT_11N() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = nibble2(opcodeUnit); - int literal = (nibble3(opcodeUnit) << 28) >> 28; // sign-extend - return new OneRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcodeUnit(), - makeByte(insn.getA(), insn.getLiteralNibble()))); - } - }, - - FORMAT_11X() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - return new OneRegisterDecodedInstruction( - this, opcode, 0, null, - 0, 0L, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write(codeUnit(insn.getOpcode(), insn.getA())); - } - }, - - FORMAT_10T() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int baseAddress = in.cursor() - 1; - int opcode = byte0(opcodeUnit); - int target = (byte) byte1(opcodeUnit); // sign-extend - return new ZeroRegisterDecodedInstruction( - this, opcode, 0, null, - baseAddress + target, 0L); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - int relativeTarget = insn.getTargetByte(out.cursor()); - out.write(codeUnit(insn.getOpcode(), relativeTarget)); - } - }, - - FORMAT_20T() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int baseAddress = in.cursor() - 1; - int opcode = byte0(opcodeUnit); - int literal = byte1(opcodeUnit); // should be zero - int target = (short) in.read(); // sign-extend - return new ZeroRegisterDecodedInstruction( - this, opcode, 0, null, - baseAddress + target, literal); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - short relativeTarget = insn.getTargetUnit(out.cursor()); - out.write(insn.getOpcodeUnit(), relativeTarget); - } - }, - - FORMAT_20BC() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - // Note: We use the literal field to hold the decoded AA value. - int opcode = byte0(opcodeUnit); - int literal = byte1(opcodeUnit); - int index = in.read(); - return new ZeroRegisterDecodedInstruction( - this, opcode, index, IndexType.VARIES, - 0, literal); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), insn.getLiteralByte()), - insn.getIndexUnit()); - } - }, - - FORMAT_22X() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int b = in.read(); - return new TwoRegisterDecodedInstruction( - this, opcode, 0, null, - 0, 0L, - a, b); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - insn.getBUnit()); - } - }, - - FORMAT_21T() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int baseAddress = in.cursor() - 1; - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int target = (short) in.read(); // sign-extend - return new OneRegisterDecodedInstruction( - this, opcode, 0, null, - baseAddress + target, 0L, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - short relativeTarget = insn.getTargetUnit(out.cursor()); - out.write(codeUnit(insn.getOpcode(), insn.getA()), relativeTarget); - } - }, - - FORMAT_21S() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int literal = (short) in.read(); // sign-extend - return new OneRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - insn.getLiteralUnit()); - } - }, - - FORMAT_21H() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - long literal = (short) in.read(); // sign-extend - - /* - * Format 21h decodes differently depending on the opcode, - * because the "signed hat" might represent either a 32- - * or 64- bit value. - */ - literal <<= (opcode == Opcodes.CONST_HIGH16) ? 16 : 48; - - return new OneRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - // See above. - int opcode = insn.getOpcode(); - int shift = (opcode == Opcodes.CONST_HIGH16) ? 16 : 48; - short literal = (short) (insn.getLiteral() >> shift); - - out.write(codeUnit(opcode, insn.getA()), literal); - } - }, - - FORMAT_21C() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int index = in.read(); - IndexType indexType = OpcodeInfo.getIndexType(opcode); - return new OneRegisterDecodedInstruction( - this, opcode, index, indexType, - 0, 0L, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - insn.getIndexUnit()); - } - }, - - FORMAT_23X() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int bc = in.read(); - int b = byte0(bc); - int c = byte1(bc); - return new ThreeRegisterDecodedInstruction( - this, opcode, 0, null, - 0, 0L, - a, b, c); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - codeUnit(insn.getB(), insn.getC())); - } - }, - - FORMAT_22B() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int bc = in.read(); - int b = byte0(bc); - int literal = (byte) byte1(bc); // sign-extend - return new TwoRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal, - a, b); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - codeUnit(insn.getB(), - insn.getLiteralByte())); - } - }, - - FORMAT_22T() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int baseAddress = in.cursor() - 1; - int opcode = byte0(opcodeUnit); - int a = nibble2(opcodeUnit); - int b = nibble3(opcodeUnit); - int target = (short) in.read(); // sign-extend - return new TwoRegisterDecodedInstruction( - this, opcode, 0, null, - baseAddress + target, 0L, - a, b); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - short relativeTarget = insn.getTargetUnit(out.cursor()); - out.write( - codeUnit(insn.getOpcode(), - makeByte(insn.getA(), insn.getB())), - relativeTarget); - } - }, - - FORMAT_22S() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = nibble2(opcodeUnit); - int b = nibble3(opcodeUnit); - int literal = (short) in.read(); // sign-extend - return new TwoRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal, - a, b); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), - makeByte(insn.getA(), insn.getB())), - insn.getLiteralUnit()); - } - }, - - FORMAT_22C() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = nibble2(opcodeUnit); - int b = nibble3(opcodeUnit); - int index = in.read(); - IndexType indexType = OpcodeInfo.getIndexType(opcode); - return new TwoRegisterDecodedInstruction( - this, opcode, index, indexType, - 0, 0L, - a, b); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), - makeByte(insn.getA(), insn.getB())), - insn.getIndexUnit()); - } - }, - - FORMAT_22CS() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = nibble2(opcodeUnit); - int b = nibble3(opcodeUnit); - int index = in.read(); - return new TwoRegisterDecodedInstruction( - this, opcode, index, IndexType.FIELD_OFFSET, - 0, 0L, - a, b); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write( - codeUnit(insn.getOpcode(), - makeByte(insn.getA(), insn.getB())), - insn.getIndexUnit()); - } - }, - - FORMAT_30T() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int baseAddress = in.cursor() - 1; - int opcode = byte0(opcodeUnit); - int literal = byte1(opcodeUnit); // should be zero - int target = in.readInt(); - return new ZeroRegisterDecodedInstruction( - this, opcode, 0, null, - baseAddress + target, literal); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - int relativeTarget = insn.getTarget(out.cursor()); - out.write(insn.getOpcodeUnit(), - unit0(relativeTarget), unit1(relativeTarget)); - } - }, - - FORMAT_32X() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int literal = byte1(opcodeUnit); // should be zero - int a = in.read(); - int b = in.read(); - return new TwoRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal, - a, b); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - out.write(insn.getOpcodeUnit(), insn.getAUnit(), insn.getBUnit()); - } - }, - - FORMAT_31I() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int literal = in.readInt(); - return new OneRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - int literal = insn.getLiteralInt(); - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - unit0(literal), - unit1(literal)); - } - }, - - FORMAT_31T() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int baseAddress = in.cursor() - 1; - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int target = baseAddress + in.readInt(); - - /* - * Switch instructions need to "forward" their addresses to their - * payload target instructions. - */ - switch (opcode) { - case Opcodes.PACKED_SWITCH: - case Opcodes.SPARSE_SWITCH: { - in.setBaseAddress(target, baseAddress); - break; - } - } - - return new OneRegisterDecodedInstruction( - this, opcode, 0, null, - target, 0L, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - int relativeTarget = insn.getTarget(out.cursor()); - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - unit0(relativeTarget), unit1(relativeTarget)); - } - }, - - FORMAT_31C() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - int index = in.readInt(); - IndexType indexType = OpcodeInfo.getIndexType(opcode); - return new OneRegisterDecodedInstruction( - this, opcode, index, indexType, - 0, 0L, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - int index = insn.getIndex(); - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - unit0(index), - unit1(index)); - } - }, - - FORMAT_35C() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - return decodeRegisterList(this, opcodeUnit, in); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - encodeRegisterList(insn, out); - } - }, - - FORMAT_35MS() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - return decodeRegisterList(this, opcodeUnit, in); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - encodeRegisterList(insn, out); - } - }, - - FORMAT_35MI() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - return decodeRegisterList(this, opcodeUnit, in); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - encodeRegisterList(insn, out); - } - }, - - FORMAT_3RC() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - return decodeRegisterRange(this, opcodeUnit, in); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - encodeRegisterRange(insn, out); - } - }, - - FORMAT_3RMS() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - return decodeRegisterRange(this, opcodeUnit, in); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - encodeRegisterRange(insn, out); - } - }, - - FORMAT_3RMI() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - return decodeRegisterRange(this, opcodeUnit, in); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - encodeRegisterRange(insn, out); - } - }, - - FORMAT_51L() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int opcode = byte0(opcodeUnit); - int a = byte1(opcodeUnit); - long literal = in.readLong(); - return new OneRegisterDecodedInstruction( - this, opcode, 0, null, - 0, literal, - a); - } - - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - long literal = insn.getLiteral(); - out.write( - codeUnit(insn.getOpcode(), insn.getA()), - unit0(literal), - unit1(literal), - unit2(literal), - unit3(literal)); - } - }, - - FORMAT_PACKED_SWITCH_PAYLOAD() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int size = in.read(); - int firstKey = in.readInt(); - int[] targets = new int[size]; - - for (int i = 0; i < size; i++) { - targets[i] = in.readInt(); - } + FORMAT_00X() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + return new ZeroRegisterDecodedInstruction(this, opcodeUnit, 0, null, 0, 0L); + } - return new PackedSwitchPayloadDecodedInstruction( - this, opcodeUnit, firstKey, targets); - } + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(insn.getOpcodeUnit()); + } + }, + + FORMAT_10X() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int literal = byte1(opcodeUnit); // should be zero + return new ZeroRegisterDecodedInstruction(this, opcode, 0, null, 0, literal); + } - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - PackedSwitchPayloadDecodedInstruction payload = - (PackedSwitchPayloadDecodedInstruction) insn; - int[] targets = payload.getTargets(); + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(insn.getOpcodeUnit()); + } + }, + + FORMAT_12X() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = nibble2(opcodeUnit); + int b = nibble3(opcodeUnit); + return new TwoRegisterDecodedInstruction(this, opcode, 0, null, 0, 0L, a, b); + } - out.write(payload.getOpcodeUnit()); - out.write(asUnsignedUnit(targets.length)); - out.writeInt(payload.getFirstKey()); + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcodeUnit(), makeByte(insn.getA(), insn.getB()))); + } + }, + + FORMAT_11N() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = nibble2(opcodeUnit); + int literal = (nibble3(opcodeUnit) << 28) >> 28; // sign-extend + return new OneRegisterDecodedInstruction(this, opcode, 0, null, 0, literal, a); + } - for (int target : targets) { - out.writeInt(target); - } - } - }, + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcodeUnit(), makeByte(insn.getA(), insn.getLiteralNibble()))); + } + }, + + FORMAT_11X() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + return new OneRegisterDecodedInstruction(this, opcode, 0, null, 0, 0L, a); + } - FORMAT_SPARSE_SWITCH_PAYLOAD() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int size = in.read(); - int[] keys = new int[size]; - int[] targets = new int[size]; + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), insn.getA())); + } + }, + + FORMAT_10T() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int baseAddress = in.cursor() - 1; + int opcode = byte0(opcodeUnit); + int target = (byte) byte1(opcodeUnit); // sign-extend + return new ZeroRegisterDecodedInstruction(this, opcode, 0, null, baseAddress + target, 0L); + } - for (int i = 0; i < size; i++) { - keys[i] = in.readInt(); - } + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + int relativeTarget = insn.getTargetByte(out.cursor()); + out.write(codeUnit(insn.getOpcode(), relativeTarget)); + } + }, + + FORMAT_20T() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int baseAddress = in.cursor() - 1; + int opcode = byte0(opcodeUnit); + int literal = byte1(opcodeUnit); // should be zero + int target = (short) in.read(); // sign-extend + return new ZeroRegisterDecodedInstruction(this, + opcode, + 0, + null, + baseAddress + target, + literal); + } - for (int i = 0; i < size; i++) { - targets[i] = in.readInt(); - } + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + short relativeTarget = insn.getTargetUnit(out.cursor()); + out.write(insn.getOpcodeUnit(), relativeTarget); + } +}, + + FORMAT_20BC() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + // Note: We use the literal field to hold the decoded AA value. + int opcode = byte0(opcodeUnit); + int literal = byte1(opcodeUnit); + int index = in.read(); + return new ZeroRegisterDecodedInstruction(this, opcode, index, IndexType.VARIES, 0, literal); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), insn.getLiteralByte()), insn.getIndexUnit()); + } +}, + + FORMAT_22X() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int b = in.read(); + return new TwoRegisterDecodedInstruction(this, opcode, 0, null, 0, 0L, a, b); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), insn.getA()), insn.getBUnit()); + } +}, + + FORMAT_21T() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int baseAddress = in.cursor() - 1; + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int target = (short) in.read(); // sign-extend + return new OneRegisterDecodedInstruction(this, opcode, 0, null, baseAddress + target, 0L, a); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + short relativeTarget = insn.getTargetUnit(out.cursor()); + out.write(codeUnit(insn.getOpcode(), insn.getA()), relativeTarget); + } +}, + + FORMAT_21S() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int literal = (short) in.read(); // sign-extend + return new OneRegisterDecodedInstruction(this, opcode, 0, null, 0, literal, a); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), insn.getA()), insn.getLiteralUnit()); + } +}, + + FORMAT_21H() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + long literal = (short) in.read(); // sign-extend + + /* + * Format 21h decodes differently depending on the opcode, + * because the "signed hat" might represent either a 32- + * or 64- bit value. + */ + literal <<= (opcode == Opcodes.CONST_HIGH16) ? 16 : 48; + + return new OneRegisterDecodedInstruction(this, opcode, 0, null, 0, literal, a); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + // See above. + int opcode = insn.getOpcode(); + int shift = (opcode == Opcodes.CONST_HIGH16) ? 16 : 48; + short literal = (short) (insn.getLiteral() >> shift); + + out.write(codeUnit(opcode, insn.getA()), literal); + } +}, + + FORMAT_21C() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int index = in.read(); + IndexType indexType = OpcodeInfo.getIndexType(opcode); + return new OneRegisterDecodedInstruction(this, opcode, index, indexType, 0, 0L, a); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), insn.getA()), insn.getIndexUnit()); + } +}, + + FORMAT_23X() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int bc = in.read(); + int b = byte0(bc); + int c = byte1(bc); + return new ThreeRegisterDecodedInstruction(this, opcode, 0, null, 0, 0L, a, b, c); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), insn.getA()), codeUnit(insn.getB(), insn.getC())); + } +}, + + FORMAT_22B() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int bc = in.read(); + int b = byte0(bc); + int literal = (byte) byte1(bc); // sign-extend + return new TwoRegisterDecodedInstruction(this, opcode, 0, null, 0, literal, a, b); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), insn.getA()), + codeUnit(insn.getB(), insn.getLiteralByte())); + } +}, + + FORMAT_22T() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int baseAddress = in.cursor() - 1; + int opcode = byte0(opcodeUnit); + int a = nibble2(opcodeUnit); + int b = nibble3(opcodeUnit); + int target = (short) in.read(); // sign-extend + return new TwoRegisterDecodedInstruction(this, opcode, 0, null, baseAddress + target, 0L, a, b); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + short relativeTarget = insn.getTargetUnit(out.cursor()); + out.write(codeUnit(insn.getOpcode(), makeByte(insn.getA(), insn.getB())), relativeTarget); + } +}, + + FORMAT_22S() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = nibble2(opcodeUnit); + int b = nibble3(opcodeUnit); + int literal = (short) in.read(); // sign-extend + return new TwoRegisterDecodedInstruction(this, opcode, 0, null, 0, literal, a, b); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), makeByte(insn.getA(), insn.getB())), + insn.getLiteralUnit()); + } +}, + + FORMAT_22C() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = nibble2(opcodeUnit); + int b = nibble3(opcodeUnit); + int index = in.read(); + IndexType indexType = OpcodeInfo.getIndexType(opcode); + return new TwoRegisterDecodedInstruction(this, opcode, index, indexType, 0, 0L, a, b); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), makeByte(insn.getA(), insn.getB())), insn.getIndexUnit()); + } +}, + + FORMAT_22CS() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = nibble2(opcodeUnit); + int b = nibble3(opcodeUnit); + int index = in.read(); + return new TwoRegisterDecodedInstruction(this, + opcode, + index, + IndexType.FIELD_OFFSET, + 0, + 0L, + a, + b); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), makeByte(insn.getA(), insn.getB())), insn.getIndexUnit()); + } +}, + + FORMAT_30T() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int baseAddress = in.cursor() - 1; + int opcode = byte0(opcodeUnit); + int literal = byte1(opcodeUnit); // should be zero + int target = in.readInt(); + return new ZeroRegisterDecodedInstruction(this, opcode, 0, null, baseAddress + target, literal); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + int relativeTarget = insn.getTarget(out.cursor()); + out.write(insn.getOpcodeUnit(), unit0(relativeTarget), unit1(relativeTarget)); + } +}, + + FORMAT_32X() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int literal = byte1(opcodeUnit); // should be zero + int a = in.read(); + int b = in.read(); + return new TwoRegisterDecodedInstruction(this, opcode, 0, null, 0, literal, a, b); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + out.write(insn.getOpcodeUnit(), insn.getAUnit(), insn.getBUnit()); + } +}, + + FORMAT_31I() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int literal = in.readInt(); + return new OneRegisterDecodedInstruction(this, opcode, 0, null, 0, literal, a); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + int literal = insn.getLiteralInt(); + out.write(codeUnit(insn.getOpcode(), insn.getA()), unit0(literal), unit1(literal)); + } +}, + + FORMAT_31T() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int baseAddress = in.cursor() - 1; + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int target = baseAddress + in.readInt(); + + /* + * Switch instructions need to "forward" their addresses to their + * payload target instructions. + */ + switch (opcode) { + case Opcodes.PACKED_SWITCH: + case Opcodes.SPARSE_SWITCH: { + in.setBaseAddress(target, baseAddress); + break; + } + } - return new SparseSwitchPayloadDecodedInstruction( - this, opcodeUnit, keys, targets); - } + return new OneRegisterDecodedInstruction(this, opcode, 0, null, target, 0L, a); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + int relativeTarget = insn.getTarget(out.cursor()); + out.write(codeUnit(insn.getOpcode(), insn.getA()), unit0(relativeTarget), + unit1(relativeTarget)); + } +}, + + FORMAT_31C() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + int index = in.readInt(); + IndexType indexType = OpcodeInfo.getIndexType(opcode); + return new OneRegisterDecodedInstruction(this, opcode, index, indexType, 0, 0L, a); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + int index = insn.getIndex(); + out.write(codeUnit(insn.getOpcode(), insn.getA()), unit0(index), unit1(index)); + } +}, + + FORMAT_35C() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + return decodeRegisterList(this, opcodeUnit, in); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + encodeRegisterList(insn, out); + } +}, + + FORMAT_35MS() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + return decodeRegisterList(this, opcodeUnit, in); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + encodeRegisterList(insn, out); + } +}, + + FORMAT_35MI() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + return decodeRegisterList(this, opcodeUnit, in); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + encodeRegisterList(insn, out); + } +}, + + FORMAT_3RC() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + return decodeRegisterRange(this, opcodeUnit, in); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + encodeRegisterRange(insn, out); + } +}, + + FORMAT_3RMS() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + return decodeRegisterRange(this, opcodeUnit, in); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + encodeRegisterRange(insn, out); + } +}, + + FORMAT_3RMI() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + return decodeRegisterRange(this, opcodeUnit, in); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + encodeRegisterRange(insn, out); + } +}, + + FORMAT_51L() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int a = byte1(opcodeUnit); + long literal = in.readLong(); + return new OneRegisterDecodedInstruction(this, opcode, 0, null, 0, literal, a); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + long literal = insn.getLiteral(); + out.write(codeUnit(insn.getOpcode(), insn.getA()), unit0(literal), unit1(literal), + unit2(literal), unit3(literal)); + } +}, + + FORMAT_PACKED_SWITCH_PAYLOAD() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int size = in.read(); + int firstKey = in.readInt(); + int[] targets = new int[size]; + + for (int i = 0; i < size; i++) { + targets[i] = in.readInt(); + } - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - SparseSwitchPayloadDecodedInstruction payload = - (SparseSwitchPayloadDecodedInstruction) insn; - int[] keys = payload.getKeys(); - int[] targets = payload.getTargets(); + return new PackedSwitchPayloadDecodedInstruction(this, opcodeUnit, firstKey, targets); + } - out.write(payload.getOpcodeUnit()); - out.write(asUnsignedUnit(targets.length)); + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + PackedSwitchPayloadDecodedInstruction payload = (PackedSwitchPayloadDecodedInstruction) insn; + int[] targets = payload.getTargets(); - for (int key : keys) { - out.writeInt(key); - } + out.write(payload.getOpcodeUnit()); + out.write(asUnsignedUnit(targets.length)); + out.writeInt(payload.getFirstKey()); - for (int target : targets) { - out.writeInt(target); - } - } - }, - - FORMAT_FILL_ARRAY_DATA_PAYLOAD() { - @Override public DecodedInstruction decode(int opcodeUnit, - CodeInput in) throws EOFException { - int elementWidth = in.read(); - int size = in.readInt(); - - switch (elementWidth) { - case 1: { - byte[] array = new byte[size]; - boolean even = true; - for (int i = 0, value = 0; i < size; i++, even = !even) { - if (even) { - value = in.read(); - } - array[i] = (byte) (value & 0xff); - value >>= 8; - } - return new FillArrayDataPayloadDecodedInstruction( - this, opcodeUnit, array); - } - case 2: { - short[] array = new short[size]; - for (int i = 0; i < size; i++) { - array[i] = (short) in.read(); - } - return new FillArrayDataPayloadDecodedInstruction( - this, opcodeUnit, array); - } - case 4: { - int[] array = new int[size]; - for (int i = 0; i < size; i++) { - array[i] = in.readInt(); - } - return new FillArrayDataPayloadDecodedInstruction( - this, opcodeUnit, array); - } - case 8: { - long[] array = new long[size]; - for (int i = 0; i < size; i++) { - array[i] = in.readLong(); - } - return new FillArrayDataPayloadDecodedInstruction( - this, opcodeUnit, array); - } - } - - throw new DexException("bogus element_width: " - + Hex.u2(elementWidth)); - } + for (int target : targets) { + out.writeInt(target); + } + } +}, + + FORMAT_SPARSE_SWITCH_PAYLOAD() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int size = in.read(); + int[] keys = new int[size]; + int[] targets = new int[size]; + + for (int i = 0; i < size; i++) { + keys[i] = in.readInt(); + } - @Override public void encode(DecodedInstruction insn, CodeOutput out) { - FillArrayDataPayloadDecodedInstruction payload = - (FillArrayDataPayloadDecodedInstruction) insn; - short elementWidth = payload.getElementWidthUnit(); - Object data = payload.getData(); - - out.write(payload.getOpcodeUnit()); - out.write(elementWidth); - out.writeInt(payload.getSize()); - - switch (elementWidth) { - case 1: out.write((byte[]) data); break; - case 2: out.write((short[]) data); break; - case 4: out.write((int[]) data); break; - case 8: out.write((long[]) data); break; - default: { - throw new DexException("bogus element_width: " - + Hex.u2(elementWidth)); - } - } - } - }; + for (int i = 0; i < size; i++) { + targets[i] = in.readInt(); + } - /** - * Decodes an instruction specified by the given opcode unit, reading - * any required additional code units from the given input source. - */ - public abstract DecodedInstruction decode(int opcodeUnit, CodeInput in) - throws EOFException; + return new SparseSwitchPayloadDecodedInstruction(this, opcodeUnit, keys, targets); + } - /** - * Encodes the given instruction. - */ - public abstract void encode(DecodedInstruction insn, CodeOutput out); + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + SparseSwitchPayloadDecodedInstruction payload = (SparseSwitchPayloadDecodedInstruction) insn; + int[] keys = payload.getKeys(); + int[] targets = payload.getTargets(); - /** - * Helper method that decodes any of the register-list formats. - */ - private static DecodedInstruction decodeRegisterList( - InstructionCodec format, int opcodeUnit, CodeInput in) - throws EOFException { - int opcode = byte0(opcodeUnit); - int e = nibble2(opcodeUnit); - int registerCount = nibble3(opcodeUnit); - int index = in.read(); - int abcd = in.read(); - int a = nibble0(abcd); - int b = nibble1(abcd); - int c = nibble2(abcd); - int d = nibble3(abcd); - IndexType indexType = OpcodeInfo.getIndexType(opcode); - - // TODO: Having to switch like this is less than ideal. - switch (registerCount) { - case 0: - return new ZeroRegisterDecodedInstruction( - format, opcode, index, indexType, - 0, 0L); - case 1: - return new OneRegisterDecodedInstruction( - format, opcode, index, indexType, - 0, 0L, - a); - case 2: - return new TwoRegisterDecodedInstruction( - format, opcode, index, indexType, - 0, 0L, - a, b); - case 3: - return new ThreeRegisterDecodedInstruction( - format, opcode, index, indexType, - 0, 0L, - a, b, c); - case 4: - return new FourRegisterDecodedInstruction( - format, opcode, index, indexType, - 0, 0L, - a, b, c, d); - case 5: - return new FiveRegisterDecodedInstruction( - format, opcode, index, indexType, - 0, 0L, - a, b, c, d, e); - } + out.write(payload.getOpcodeUnit()); + out.write(asUnsignedUnit(targets.length)); - throw new DexException("bogus registerCount: " - + Hex.uNibble(registerCount)); + for (int key : keys) { + out.writeInt(key); } - /** - * Helper method that encodes any of the register-list formats. - */ - private static void encodeRegisterList(DecodedInstruction insn, - CodeOutput out) { - out.write(codeUnit(insn.getOpcode(), - makeByte(insn.getE(), insn.getRegisterCount())), - insn.getIndexUnit(), - codeUnit(insn.getA(), insn.getB(), insn.getC(), insn.getD())); + for (int target : targets) { + out.writeInt(target); } - - /** - * Helper method that decodes any of the three-unit register-range formats. - */ - private static DecodedInstruction decodeRegisterRange( - InstructionCodec format, int opcodeUnit, CodeInput in) - throws EOFException { - int opcode = byte0(opcodeUnit); - int registerCount = byte1(opcodeUnit); - int index = in.read(); - int a = in.read(); - IndexType indexType = OpcodeInfo.getIndexType(opcode); - return new RegisterRangeDecodedInstruction( - format, opcode, index, indexType, - 0, 0L, - a, registerCount); - } - - /** - * Helper method that encodes any of the three-unit register-range formats. - */ - private static void encodeRegisterRange(DecodedInstruction insn, - CodeOutput out) { - out.write(codeUnit(insn.getOpcode(), insn.getRegisterCount()), - insn.getIndexUnit(), - insn.getAUnit()); + } +}, + + FORMAT_FILL_ARRAY_DATA_PAYLOAD() { + @Override + public DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException { + int elementWidth = in.read(); + int size = in.readInt(); + + switch (elementWidth) { + case 1: { + byte[] array = new byte[size]; + boolean even = true; + for (int i = 0, value = 0; i < size; i++, even = !even) { + if (even) { + value = in.read(); + } + array[i] = (byte) (value & 0xff); + value >>= 8; + } + return new FillArrayDataPayloadDecodedInstruction(this, opcodeUnit, array); + } + case 2: { + short[] array = new short[size]; + for (int i = 0; i < size; i++) { + array[i] = (short) in.read(); + } + return new FillArrayDataPayloadDecodedInstruction(this, opcodeUnit, array); + } + case 4: { + int[] array = new int[size]; + for (int i = 0; i < size; i++) { + array[i] = in.readInt(); + } + return new FillArrayDataPayloadDecodedInstruction(this, opcodeUnit, array); + } + case 8: { + long[] array = new long[size]; + for (int i = 0; i < size; i++) { + array[i] = in.readLong(); + } + return new FillArrayDataPayloadDecodedInstruction(this, opcodeUnit, array); + } } - private static short codeUnit(int lowByte, int highByte) { - if ((lowByte & ~0xff) != 0) { - throw new IllegalArgumentException("bogus lowByte"); - } - - if ((highByte & ~0xff) != 0) { - throw new IllegalArgumentException("bogus highByte"); - } - - return (short) (lowByte | (highByte << 8)); + throw new DexException("bogus element_width: " + Hex.u2(elementWidth)); + } + + @Override + public void encode(DecodedInstruction insn, CodeOutput out) { + FillArrayDataPayloadDecodedInstruction payload = (FillArrayDataPayloadDecodedInstruction) insn; + short elementWidth = payload.getElementWidthUnit(); + Object data = payload.getData(); + + out.write(payload.getOpcodeUnit()); + out.write(elementWidth); + out.writeInt(payload.getSize()); + + switch (elementWidth) { + case 1: + out.write((byte[]) data); + break; + case 2: + out.write((short[]) data); + break; + case 4: + out.write((int[]) data); + break; + case 8: + out.write((long[]) data); + break; + default: { + throw new DexException("bogus element_width: " + Hex.u2(elementWidth)); + } + } + } +}; + + /** + * Decodes an instruction specified by the given opcode unit, reading + * any required additional code units from the given input source. + */ + public abstract DecodedInstruction decode(int opcodeUnit, CodeInput in) throws EOFException; + + /** + * Encodes the given instruction. + */ + public abstract void encode(DecodedInstruction insn, CodeOutput out); + + /** + * Helper method that decodes any of the register-list formats. + */ + private static DecodedInstruction decodeRegisterList(InstructionCodec format, int opcodeUnit, + CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int e = nibble2(opcodeUnit); + int registerCount = nibble3(opcodeUnit); + int index = in.read(); + int abcd = in.read(); + int a = nibble0(abcd); + int b = nibble1(abcd); + int c = nibble2(abcd); + int d = nibble3(abcd); + IndexType indexType = OpcodeInfo.getIndexType(opcode); + + // TODO(dx team): Having to switch like this is less than ideal. + switch (registerCount) { + case 0: + return new ZeroRegisterDecodedInstruction(format, opcode, index, indexType, 0, 0L); + case 1: + return new OneRegisterDecodedInstruction(format, opcode, index, indexType, 0, 0L, a); + case 2: + return new TwoRegisterDecodedInstruction(format, opcode, index, indexType, 0, 0L, a, b); + case 3: + return new ThreeRegisterDecodedInstruction(format, + opcode, + index, + indexType, + 0, + 0L, + a, + b, + c); + case 4: + return new FourRegisterDecodedInstruction(format, + opcode, + index, + indexType, + 0, + 0L, + a, + b, + c, + d); + case 5: + return new FiveRegisterDecodedInstruction(format, + opcode, + index, + indexType, + 0, + 0L, + a, + b, + c, + d, + e); } - private static short codeUnit(int nibble0, int nibble1, int nibble2, - int nibble3) { - if ((nibble0 & ~0xf) != 0) { - throw new IllegalArgumentException("bogus nibble0"); - } + throw new DexException("bogus registerCount: " + Hex.uNibble(registerCount)); + } + + /** + * Helper method that encodes any of the register-list formats. + */ + private static void encodeRegisterList(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), makeByte(insn.getE(), insn.getRegisterCount())), + insn.getIndexUnit(), codeUnit(insn.getA(), insn.getB(), insn.getC(), insn.getD())); + } + + /** + * Helper method that decodes any of the three-unit register-range formats. + */ + private static DecodedInstruction decodeRegisterRange(InstructionCodec format, int opcodeUnit, + CodeInput in) throws EOFException { + int opcode = byte0(opcodeUnit); + int registerCount = byte1(opcodeUnit); + int index = in.read(); + int a = in.read(); + IndexType indexType = OpcodeInfo.getIndexType(opcode); + return new RegisterRangeDecodedInstruction(format, + opcode, + index, + indexType, + 0, + 0L, + a, + registerCount); + } + + /** + * Helper method that encodes any of the three-unit register-range formats. + */ + private static void encodeRegisterRange(DecodedInstruction insn, CodeOutput out) { + out.write(codeUnit(insn.getOpcode(), insn.getRegisterCount()), insn.getIndexUnit(), + insn.getAUnit()); + } + + private static short codeUnit(int lowByte, int highByte) { + if ((lowByte & ~0xff) != 0) { + throw new IllegalArgumentException("bogus lowByte"); + } - if ((nibble1 & ~0xf) != 0) { - throw new IllegalArgumentException("bogus nibble1"); - } + if ((highByte & ~0xff) != 0) { + throw new IllegalArgumentException("bogus highByte"); + } - if ((nibble2 & ~0xf) != 0) { - throw new IllegalArgumentException("bogus nibble2"); - } + return (short) (lowByte | (highByte << 8)); + } - if ((nibble3 & ~0xf) != 0) { - throw new IllegalArgumentException("bogus nibble3"); - } - - return (short) (nibble0 | (nibble1 << 4) - | (nibble2 << 8) | (nibble3 << 12)); + private static short codeUnit(int nibble0, int nibble1, int nibble2, int nibble3) { + if ((nibble0 & ~0xf) != 0) { + throw new IllegalArgumentException("bogus nibble0"); } - private static int makeByte(int lowNibble, int highNibble) { - if ((lowNibble & ~0xf) != 0) { - throw new IllegalArgumentException("bogus lowNibble"); - } + if ((nibble1 & ~0xf) != 0) { + throw new IllegalArgumentException("bogus nibble1"); + } - if ((highNibble & ~0xf) != 0) { - throw new IllegalArgumentException("bogus highNibble"); - } + if ((nibble2 & ~0xf) != 0) { + throw new IllegalArgumentException("bogus nibble2"); + } - return lowNibble | (highNibble << 4); + if ((nibble3 & ~0xf) != 0) { + throw new IllegalArgumentException("bogus nibble3"); } - private static short asUnsignedUnit(int value) { - if ((value & ~0xffff) != 0) { - throw new IllegalArgumentException("bogus unsigned code unit"); - } + return (short) (nibble0 | (nibble1 << 4) | (nibble2 << 8) | (nibble3 << 12)); + } - return (short) value; + private static int makeByte(int lowNibble, int highNibble) { + if ((lowNibble & ~0xf) != 0) { + throw new IllegalArgumentException("bogus lowNibble"); } - private static short unit0(int value) { - return (short) value; + if ((highNibble & ~0xf) != 0) { + throw new IllegalArgumentException("bogus highNibble"); } - private static short unit1(int value) { - return (short) (value >> 16); - } + return lowNibble | (highNibble << 4); + } - private static short unit0(long value) { - return (short) value; + private static short asUnsignedUnit(int value) { + if ((value & ~0xffff) != 0) { + throw new IllegalArgumentException("bogus unsigned code unit"); } - private static short unit1(long value) { - return (short) (value >> 16); - } + return (short) value; + } - private static short unit2(long value) { - return (short) (value >> 32); - } + private static short unit0(int value) { + return (short) value; + } - private static short unit3(long value) { - return (short) (value >> 48); - } + private static short unit1(int value) { + return (short) (value >> 16); + } - private static int byte0(int value) { - return value & 0xff; - } + private static short unit0(long value) { + return (short) value; + } - private static int byte1(int value) { - return (value >> 8) & 0xff; - } + private static short unit1(long value) { + return (short) (value >> 16); + } - private static int byte2(int value) { - return (value >> 16) & 0xff; - } + private static short unit2(long value) { + return (short) (value >> 32); + } - private static int byte3(int value) { - return value >>> 24; - } + private static short unit3(long value) { + return (short) (value >> 48); + } - private static int nibble0(int value) { - return value & 0xf; - } + private static int byte0(int value) { + return value & 0xff; + } - private static int nibble1(int value) { - return (value >> 4) & 0xf; - } + private static int byte1(int value) { + return (value >> 8) & 0xff; + } - private static int nibble2(int value) { - return (value >> 8) & 0xf; - } + private static int nibble0(int value) { + return value & 0xf; + } - private static int nibble3(int value) { - return (value >> 12) & 0xf; - } + private static int nibble1(int value) { + return (value >> 4) & 0xf; + } + + private static int nibble2(int value) { + return (value >> 8) & 0xf; + } + + private static int nibble3(int value) { + return (value >> 12) & 0xf; + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/OneRegisterDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/OneRegisterDecodedInstruction.java index acde53a..cac4d96 100644 --- a/dx/src/com/android/jack/dx/io/instructions/OneRegisterDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/OneRegisterDecodedInstruction.java @@ -22,34 +22,45 @@ import com.android.jack.dx.io.IndexType; * A decoded Dalvik instruction which has one register argument. */ public final class OneRegisterDecodedInstruction extends DecodedInstruction { - /** register argument "A" */ - private final int a; - - /** - * Constructs an instance. - */ - public OneRegisterDecodedInstruction(InstructionCodec format, int opcode, - int index, IndexType indexType, int target, long literal, - int a) { - super(format, opcode, index, indexType, target, literal); - - this.a = a; - } - - /** @inheritDoc */ - public int getRegisterCount() { - return 1; - } - - /** @inheritDoc */ - public int getA() { - return a; - } - - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - return new OneRegisterDecodedInstruction( - getFormat(), getOpcode(), newIndex, getIndexType(), - getTarget(), getLiteral(), a); - } + /** register argument "A" */ + private final int a; + + /** + * Constructs an instance. + */ + public OneRegisterDecodedInstruction(InstructionCodec format, + int opcode, + int index, + IndexType indexType, + int target, + long literal, + int a) { + super(format, opcode, index, indexType, target, literal); + + this.a = a; + } + + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 1; + } + + /** @inheritDoc */ + @Override + public int getA() { + return a; + } + + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + return new OneRegisterDecodedInstruction(getFormat(), + getOpcode(), + newIndex, + getIndexType(), + getTarget(), + getLiteral(), + a); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/PackedSwitchPayloadDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/PackedSwitchPayloadDecodedInstruction.java index f2b5049..0b2ee32 100644 --- a/dx/src/com/android/jack/dx/io/instructions/PackedSwitchPayloadDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/PackedSwitchPayloadDecodedInstruction.java @@ -20,43 +20,44 @@ package com.android.jack.dx.io.instructions; * A decoded Dalvik instruction which contains the payload for * a {@code packed-switch} instruction. */ -public final class PackedSwitchPayloadDecodedInstruction - extends DecodedInstruction { - /** first key value */ - private final int firstKey; - - /** - * array of target addresses. These are absolute, not relative, - * addresses. - */ - private final int[] targets; - - /** - * Constructs an instance. - */ - public PackedSwitchPayloadDecodedInstruction(InstructionCodec format, - int opcode, int firstKey, int[] targets) { - super(format, opcode, 0, null, 0, 0L); - - this.firstKey = firstKey; - this.targets = targets; - } - - /** @inheritDoc */ - public int getRegisterCount() { - return 0; - } - - public int getFirstKey() { - return firstKey; - } - - public int[] getTargets() { - return targets; - } - - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - throw new UnsupportedOperationException("no index in instruction"); - } +public final class PackedSwitchPayloadDecodedInstruction extends DecodedInstruction { + /** first key value */ + private final int firstKey; + + /** + * array of target addresses. These are absolute, not relative, + * addresses. + */ + private final int[] targets; + + /** + * Constructs an instance. + */ + public PackedSwitchPayloadDecodedInstruction(InstructionCodec format, int opcode, int firstKey, + int[] targets) { + super(format, opcode, 0, null, 0, 0L); + + this.firstKey = firstKey; + this.targets = targets; + } + + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 0; + } + + public int getFirstKey() { + return firstKey; + } + + public int[] getTargets() { + return targets; + } + + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + throw new UnsupportedOperationException("no index in instruction"); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/RegisterRangeDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/RegisterRangeDecodedInstruction.java index 30f1242..eb73d9b 100644 --- a/dx/src/com/android/jack/dx/io/instructions/RegisterRangeDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/RegisterRangeDecodedInstruction.java @@ -23,38 +23,51 @@ import com.android.jack.dx.io.IndexType; * "A" start register and a register count). */ public final class RegisterRangeDecodedInstruction extends DecodedInstruction { - /** register argument "A" */ - private final int a; - - /** register count */ - private final int registerCount; - - /** - * Constructs an instance. - */ - public RegisterRangeDecodedInstruction(InstructionCodec format, int opcode, - int index, IndexType indexType, int target, long literal, - int a, int registerCount) { - super(format, opcode, index, indexType, target, literal); - - this.a = a; - this.registerCount = registerCount; - } - - /** @inheritDoc */ - public int getRegisterCount() { - return registerCount; - } - - /** @inheritDoc */ - public int getA() { - return a; - } - - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - return new RegisterRangeDecodedInstruction( - getFormat(), getOpcode(), newIndex, getIndexType(), - getTarget(), getLiteral(), a, registerCount); - } + /** register argument "A" */ + private final int a; + + /** register count */ + private final int registerCount; + + /** + * Constructs an instance. + */ + public RegisterRangeDecodedInstruction(InstructionCodec format, + int opcode, + int index, + IndexType indexType, + int target, + long literal, + int a, + int registerCount) { + super(format, opcode, index, indexType, target, literal); + + this.a = a; + this.registerCount = registerCount; + } + + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return registerCount; + } + + /** @inheritDoc */ + @Override + public int getA() { + return a; + } + + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + return new RegisterRangeDecodedInstruction(getFormat(), + getOpcode(), + newIndex, + getIndexType(), + getTarget(), + getLiteral(), + a, + registerCount); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/ShortArrayCodeInput.java b/dx/src/com/android/jack/dx/io/instructions/ShortArrayCodeInput.java index c225f83..29e73ce 100644 --- a/dx/src/com/android/jack/dx/io/instructions/ShortArrayCodeInput.java +++ b/dx/src/com/android/jack/dx/io/instructions/ShortArrayCodeInput.java @@ -21,53 +21,56 @@ import java.io.EOFException; /** * Implementation of {@code CodeInput} that reads from a {@code short[]}. */ -public final class ShortArrayCodeInput extends BaseCodeCursor - implements CodeInput { - /** source array to read from */ - private final short[] array; +public final class ShortArrayCodeInput extends BaseCodeCursor implements CodeInput { + /** source array to read from */ + private final short[] array; - /** - * Constructs an instance. - */ - public ShortArrayCodeInput(short[] array) { - if (array == null) { - throw new NullPointerException("array == null"); - } - - this.array = array; + /** + * Constructs an instance. + */ + public ShortArrayCodeInput(short[] array) { + if (array == null) { + throw new NullPointerException("array == null"); } - /** @inheritDoc */ - public boolean hasMore() { - return cursor() < array.length; - } + this.array = array; + } + + /** @inheritDoc */ + @Override + public boolean hasMore() { + return cursor() < array.length; + } - /** @inheritDoc */ - public int read() throws EOFException { - try { - int value = array[cursor()]; - advance(1); - return value & 0xffff; - } catch (ArrayIndexOutOfBoundsException ex) { - throw new EOFException(); - } + /** @inheritDoc */ + @Override + public int read() throws EOFException { + try { + int value = array[cursor()]; + advance(1); + return value & 0xffff; + } catch (ArrayIndexOutOfBoundsException ex) { + throw new EOFException(); } + } - /** @inheritDoc */ - public int readInt() throws EOFException { - int short0 = read(); - int short1 = read(); + /** @inheritDoc */ + @Override + public int readInt() throws EOFException { + int short0 = read(); + int short1 = read(); - return short0 | (short1 << 16); - } + return short0 | (short1 << 16); + } - /** @inheritDoc */ - public long readLong() throws EOFException { - long short0 = read(); - long short1 = read(); - long short2 = read(); - long short3 = read(); + /** @inheritDoc */ + @Override + public long readLong() throws EOFException { + long short0 = read(); + long short1 = read(); + long short2 = read(); + long short3 = read(); - return short0 | (short1 << 16) | (short2 << 32) | (short3 << 48); - } + return short0 | (short1 << 16) | (short2 << 32) | (short3 << 48); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/ShortArrayCodeOutput.java b/dx/src/com/android/jack/dx/io/instructions/ShortArrayCodeOutput.java index 4de2e71..a1a0cd9 100644 --- a/dx/src/com/android/jack/dx/io/instructions/ShortArrayCodeOutput.java +++ b/dx/src/com/android/jack/dx/io/instructions/ShortArrayCodeOutput.java @@ -19,128 +19,138 @@ package com.android.jack.dx.io.instructions; /** * Implementation of {@code CodeOutput} that writes to a {@code short[]}. */ -public final class ShortArrayCodeOutput extends BaseCodeCursor - implements CodeOutput { - /** array to write to */ - private final short[] array; - - /** - * Constructs an instance. - * - * @param maxSize the maximum number of code units that will be written - */ - public ShortArrayCodeOutput(int maxSize) { - if (maxSize < 0) { - throw new IllegalArgumentException("maxSize < 0"); - } - - this.array = new short[maxSize]; +public final class ShortArrayCodeOutput extends BaseCodeCursor implements CodeOutput { + /** array to write to */ + private final short[] array; + + /** + * Constructs an instance. + * + * @param maxSize the maximum number of code units that will be written + */ + public ShortArrayCodeOutput(int maxSize) { + if (maxSize < 0) { + throw new IllegalArgumentException("maxSize < 0"); } - /** - * Gets the array. The returned array contains exactly the data - * written (e.g. no leftover space at the end). - */ - public short[] getArray() { - int cursor = cursor(); + this.array = new short[maxSize]; + } - if (cursor == array.length) { - return array; - } + /** + * Gets the array. The returned array contains exactly the data + * written (e.g. no leftover space at the end). + */ + public short[] getArray() { + int cursor = cursor(); - short[] result = new short[cursor]; - System.arraycopy(array, 0, result, 0, cursor); - return result; + if (cursor == array.length) { + return array; } - /** @inheritDoc */ - public void write(short codeUnit) { - array[cursor()] = codeUnit; - advance(1); - } - - /** @inheritDoc */ - public void write(short u0, short u1) { - write(u0); - write(u1); - } - - /** @inheritDoc */ - public void write(short u0, short u1, short u2) { - write(u0); - write(u1); - write(u2); - } - - /** @inheritDoc */ - public void write(short u0, short u1, short u2, short u3) { - write(u0); - write(u1); - write(u2); - write(u3); - } - - /** @inheritDoc */ - public void write(short u0, short u1, short u2, short u3, short u4) { - write(u0); - write(u1); - write(u2); - write(u3); - write(u4); - } - - /** @inheritDoc */ - public void writeInt(int value) { - write((short) value); - write((short) (value >> 16)); - } - - /** @inheritDoc */ - public void writeLong(long value) { + short[] result = new short[cursor]; + System.arraycopy(array, 0, result, 0, cursor); + return result; + } + + /** @inheritDoc */ + @Override + public void write(short codeUnit) { + array[cursor()] = codeUnit; + advance(1); + } + + /** @inheritDoc */ + @Override + public void write(short u0, short u1) { + write(u0); + write(u1); + } + + /** @inheritDoc */ + @Override + public void write(short u0, short u1, short u2) { + write(u0); + write(u1); + write(u2); + } + + /** @inheritDoc */ + @Override + public void write(short u0, short u1, short u2, short u3) { + write(u0); + write(u1); + write(u2); + write(u3); + } + + /** @inheritDoc */ + @Override + public void write(short u0, short u1, short u2, short u3, short u4) { + write(u0); + write(u1); + write(u2); + write(u3); + write(u4); + } + + /** @inheritDoc */ + @Override + public void writeInt(int value) { + write((short) value); + write((short) (value >> 16)); + } + + /** @inheritDoc */ + @Override + public void writeLong(long value) { + write((short) value); + write((short) (value >> 16)); + write((short) (value >> 32)); + write((short) (value >> 48)); + } + + /** @inheritDoc */ + @Override + public void write(byte[] data) { + int value = 0; + boolean even = true; + for (byte b : data) { + if (even) { + value = b & 0xff; + even = false; + } else { + value |= b << 8; write((short) value); - write((short) (value >> 16)); - write((short) (value >> 32)); - write((short) (value >> 48)); + even = true; + } } - /** @inheritDoc */ - public void write(byte[] data) { - int value = 0; - boolean even = true; - for (byte b : data) { - if (even) { - value = b & 0xff; - even = false; - } else { - value |= b << 8; - write((short) value); - even = true; - } - } - - if (!even) { - write((short) value); - } + if (!even) { + write((short) value); } + } - /** @inheritDoc */ - public void write(short[] data) { - for (short unit : data) { - write(unit); - } + /** @inheritDoc */ + @Override + public void write(short[] data) { + for (short unit : data) { + write(unit); } + } - /** @inheritDoc */ - public void write(int[] data) { - for (int i : data) { - writeInt(i); - } + /** @inheritDoc */ + @Override + public void write(int[] data) { + for (int i : data) { + writeInt(i); } + } - /** @inheritDoc */ - public void write(long[] data) { - for (long l : data) { - writeLong(l); - } + /** @inheritDoc */ + @Override + public void write(long[] data) { + for (long l : data) { + writeLong(l); } + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/SparseSwitchPayloadDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/SparseSwitchPayloadDecodedInstruction.java index 5155976..781c12c 100644 --- a/dx/src/com/android/jack/dx/io/instructions/SparseSwitchPayloadDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/SparseSwitchPayloadDecodedInstruction.java @@ -20,47 +20,48 @@ package com.android.jack.dx.io.instructions; * A decoded Dalvik instruction which contains the payload for * a {@code packed-switch} instruction. */ -public final class SparseSwitchPayloadDecodedInstruction - extends DecodedInstruction { - /** array of key values */ - private final int[] keys; +public final class SparseSwitchPayloadDecodedInstruction extends DecodedInstruction { + /** array of key values */ + private final int[] keys; - /** - * array of target addresses. These are absolute, not relative, - * addresses. - */ - private final int[] targets; + /** + * array of target addresses. These are absolute, not relative, + * addresses. + */ + private final int[] targets; - /** - * Constructs an instance. - */ - public SparseSwitchPayloadDecodedInstruction(InstructionCodec format, - int opcode, int[] keys, int[] targets) { - super(format, opcode, 0, null, 0, 0L); + /** + * Constructs an instance. + */ + public SparseSwitchPayloadDecodedInstruction(InstructionCodec format, int opcode, int[] keys, + int[] targets) { + super(format, opcode, 0, null, 0, 0L); - if (keys.length != targets.length) { - throw new IllegalArgumentException("keys/targets length mismatch"); - } - - this.keys = keys; - this.targets = targets; + if (keys.length != targets.length) { + throw new IllegalArgumentException("keys/targets length mismatch"); } - /** @inheritDoc */ - public int getRegisterCount() { - return 0; - } + this.keys = keys; + this.targets = targets; + } - public int[] getKeys() { - return keys; - } + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 0; + } - public int[] getTargets() { - return targets; - } + public int[] getKeys() { + return keys; + } - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - throw new UnsupportedOperationException("no index in instruction"); - } + public int[] getTargets() { + return targets; + } + + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + throw new UnsupportedOperationException("no index in instruction"); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/ThreeRegisterDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/ThreeRegisterDecodedInstruction.java index 3fe93ac..c80d827 100644 --- a/dx/src/com/android/jack/dx/io/instructions/ThreeRegisterDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/ThreeRegisterDecodedInstruction.java @@ -22,52 +22,69 @@ import com.android.jack.dx.io.IndexType; * A decoded Dalvik instruction which has three register arguments. */ public final class ThreeRegisterDecodedInstruction extends DecodedInstruction { - /** register argument "A" */ - private final int a; + /** register argument "A" */ + private final int a; - /** register argument "B" */ - private final int b; + /** register argument "B" */ + private final int b; - /** register argument "C" */ - private final int c; + /** register argument "C" */ + private final int c; - /** - * Constructs an instance. - */ - public ThreeRegisterDecodedInstruction(InstructionCodec format, int opcode, - int index, IndexType indexType, int target, long literal, - int a, int b, int c) { - super(format, opcode, index, indexType, target, literal); + /** + * Constructs an instance. + */ + public ThreeRegisterDecodedInstruction(InstructionCodec format, + int opcode, + int index, + IndexType indexType, + int target, + long literal, + int a, + int b, + int c) { + super(format, opcode, index, indexType, target, literal); - this.a = a; - this.b = b; - this.c = c; - } + this.a = a; + this.b = b; + this.c = c; + } - /** @inheritDoc */ - public int getRegisterCount() { - return 3; - } + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 3; + } - /** @inheritDoc */ - public int getA() { - return a; - } + /** @inheritDoc */ + @Override + public int getA() { + return a; + } - /** @inheritDoc */ - public int getB() { - return b; - } + /** @inheritDoc */ + @Override + public int getB() { + return b; + } - /** @inheritDoc */ - public int getC() { - return c; - } + /** @inheritDoc */ + @Override + public int getC() { + return c; + } - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - return new ThreeRegisterDecodedInstruction( - getFormat(), getOpcode(), newIndex, getIndexType(), - getTarget(), getLiteral(), a, b, c); - } + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + return new ThreeRegisterDecodedInstruction(getFormat(), + getOpcode(), + newIndex, + getIndexType(), + getTarget(), + getLiteral(), + a, + b, + c); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/TwoRegisterDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/TwoRegisterDecodedInstruction.java index 6ff4e17..2be0f98 100644 --- a/dx/src/com/android/jack/dx/io/instructions/TwoRegisterDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/TwoRegisterDecodedInstruction.java @@ -22,43 +22,57 @@ import com.android.jack.dx.io.IndexType; * A decoded Dalvik instruction which has two register arguments. */ public final class TwoRegisterDecodedInstruction extends DecodedInstruction { - /** register argument "A" */ - private final int a; + /** register argument "A" */ + private final int a; - /** register argument "B" */ - private final int b; + /** register argument "B" */ + private final int b; - /** - * Constructs an instance. - */ - public TwoRegisterDecodedInstruction(InstructionCodec format, int opcode, - int index, IndexType indexType, int target, long literal, - int a, int b) { - super(format, opcode, index, indexType, target, literal); + /** + * Constructs an instance. + */ + public TwoRegisterDecodedInstruction(InstructionCodec format, + int opcode, + int index, + IndexType indexType, + int target, + long literal, + int a, + int b) { + super(format, opcode, index, indexType, target, literal); - this.a = a; - this.b = b; - } + this.a = a; + this.b = b; + } - /** @inheritDoc */ - public int getRegisterCount() { - return 2; - } + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 2; + } - /** @inheritDoc */ - public int getA() { - return a; - } + /** @inheritDoc */ + @Override + public int getA() { + return a; + } - /** @inheritDoc */ - public int getB() { - return b; - } + /** @inheritDoc */ + @Override + public int getB() { + return b; + } - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - return new TwoRegisterDecodedInstruction( - getFormat(), getOpcode(), newIndex, getIndexType(), - getTarget(), getLiteral(), a, b); - } + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + return new TwoRegisterDecodedInstruction(getFormat(), + getOpcode(), + newIndex, + getIndexType(), + getTarget(), + getLiteral(), + a, + b); + } } diff --git a/dx/src/com/android/jack/dx/io/instructions/ZeroRegisterDecodedInstruction.java b/dx/src/com/android/jack/dx/io/instructions/ZeroRegisterDecodedInstruction.java index 0e69a5e..443b854 100644 --- a/dx/src/com/android/jack/dx/io/instructions/ZeroRegisterDecodedInstruction.java +++ b/dx/src/com/android/jack/dx/io/instructions/ZeroRegisterDecodedInstruction.java @@ -22,23 +22,32 @@ import com.android.jack.dx.io.IndexType; * A decoded Dalvik instruction which has no register arguments. */ public final class ZeroRegisterDecodedInstruction extends DecodedInstruction { - /** - * Constructs an instance. - */ - public ZeroRegisterDecodedInstruction(InstructionCodec format, int opcode, - int index, IndexType indexType, int target, long literal) { - super(format, opcode, index, indexType, target, literal); - } + /** + * Constructs an instance. + */ + public ZeroRegisterDecodedInstruction(InstructionCodec format, + int opcode, + int index, + IndexType indexType, + int target, + long literal) { + super(format, opcode, index, indexType, target, literal); + } - /** @inheritDoc */ - public int getRegisterCount() { - return 0; - } + /** @inheritDoc */ + @Override + public int getRegisterCount() { + return 0; + } - /** @inheritDoc */ - public DecodedInstruction withIndex(int newIndex) { - return new ZeroRegisterDecodedInstruction( - getFormat(), getOpcode(), newIndex, getIndexType(), - getTarget(), getLiteral()); - } + /** @inheritDoc */ + @Override + public DecodedInstruction withIndex(int newIndex) { + return new ZeroRegisterDecodedInstruction(getFormat(), + getOpcode(), + newIndex, + getIndexType(), + getTarget(), + getLiteral()); + } } diff --git a/dx/src/com/android/jack/dx/merge/CollisionPolicy.java b/dx/src/com/android/jack/dx/merge/CollisionPolicy.java index f30bf0a..1a883ab 100644 --- a/dx/src/com/android/jack/dx/merge/CollisionPolicy.java +++ b/dx/src/com/android/jack/dx/merge/CollisionPolicy.java @@ -21,14 +21,14 @@ package com.android.jack.dx.merge; */ public enum CollisionPolicy { - /** - * Keep the class def from the first dex file and discard the def from the - * second dex file. This policy is appropriate for incremental builds. - */ - KEEP_FIRST, + /** + * Keep the class def from the first dex file and discard the def from the + * second dex file. This policy is appropriate for incremental builds. + */ + KEEP_FIRST, - /** - * Forbid collisions. This policy is appropriate for merging libraries. - */ - FAIL + /** + * Forbid collisions. This policy is appropriate for merging libraries. + */ + FAIL } diff --git a/dx/src/com/android/jack/dx/merge/DexMerger.java b/dx/src/com/android/jack/dx/merge/DexMerger.java index dad2524..b473a5e 100644 --- a/dx/src/com/android/jack/dx/merge/DexMerger.java +++ b/dx/src/com/android/jack/dx/merge/DexMerger.java @@ -40,1047 +40,1070 @@ import java.util.List; * Combine two dex files into one. */ public final class DexMerger { - private final DexBuffer dexA; - private final DexBuffer dexB; - private final CollisionPolicy collisionPolicy; - private final WriterSizes writerSizes; + private final DexBuffer dexA; + private final DexBuffer dexB; + private final CollisionPolicy collisionPolicy; + private final WriterSizes writerSizes; - private final DexBuffer dexOut = new DexBuffer(); + private final DexBuffer dexOut = new DexBuffer(); - private final DexBuffer.Section headerOut; + private final DexBuffer.Section headerOut; - /** All IDs and definitions sections */ - private final DexBuffer.Section idsDefsOut; + /** All IDs and definitions sections */ + private final DexBuffer.Section idsDefsOut; - private final DexBuffer.Section mapListOut; + private final DexBuffer.Section mapListOut; - private final DexBuffer.Section typeListOut; + private final DexBuffer.Section typeListOut; - private final DexBuffer.Section classDataOut; + private final DexBuffer.Section classDataOut; - private final DexBuffer.Section codeOut; + private final DexBuffer.Section codeOut; - private final DexBuffer.Section stringDataOut; + private final DexBuffer.Section stringDataOut; - private final DexBuffer.Section debugInfoOut; + private final DexBuffer.Section debugInfoOut; - private final DexBuffer.Section encodedArrayOut; + private final DexBuffer.Section encodedArrayOut; - /** annotations directory on a type */ - private final DexBuffer.Section annotationsDirectoryOut; + /** annotations directory on a type */ + private final DexBuffer.Section annotationsDirectoryOut; - /** sets of annotations on a member, parameter or type */ - private final DexBuffer.Section annotationSetOut; + /** sets of annotations on a member, parameter or type */ + private final DexBuffer.Section annotationSetOut; - /** parameter lists */ - private final DexBuffer.Section annotationSetRefListOut; + /** parameter lists */ + private final DexBuffer.Section annotationSetRefListOut; - /** individual annotations, each containing zero or more fields */ - private final DexBuffer.Section annotationOut; + /** individual annotations, each containing zero or more fields */ + private final DexBuffer.Section annotationOut; - private final TableOfContents contentsOut; + private final TableOfContents contentsOut; - private final IndexMap aIndexMap; - private final IndexMap bIndexMap; - private final InstructionTransformer aInstructionTransformer; - private final InstructionTransformer bInstructionTransformer; + private final IndexMap aIndexMap; + private final IndexMap bIndexMap; + private final InstructionTransformer aInstructionTransformer; + private final InstructionTransformer bInstructionTransformer; - /** minimum number of wasted bytes before it's worthwhile to compact the result */ - private int compactWasteThreshold = 1024 * 1024; // 1MiB + /** minimum number of wasted bytes before it's worthwhile to compact the result */ + private int compactWasteThreshold = 1024 * 1024; // 1MiB - public DexMerger(DexBuffer dexA, DexBuffer dexB, CollisionPolicy collisionPolicy) - throws IOException { - this(dexA, dexB, collisionPolicy, new WriterSizes(dexA, dexB)); - } + public DexMerger(DexBuffer dexA, DexBuffer dexB, CollisionPolicy collisionPolicy) + throws IOException { + this(dexA, dexB, collisionPolicy, new WriterSizes(dexA, dexB)); + } - private DexMerger(DexBuffer dexA, DexBuffer dexB, CollisionPolicy collisionPolicy, - WriterSizes writerSizes) throws IOException { - this.dexA = dexA; - this.dexB = dexB; - this.collisionPolicy = collisionPolicy; - this.writerSizes = writerSizes; + private DexMerger(DexBuffer dexA, DexBuffer dexB, CollisionPolicy collisionPolicy, + WriterSizes writerSizes) throws IOException { + this.dexA = dexA; + this.dexB = dexB; + this.collisionPolicy = collisionPolicy; + this.writerSizes = writerSizes; - TableOfContents aContents = dexA.getTableOfContents(); - TableOfContents bContents = dexB.getTableOfContents(); - aIndexMap = new IndexMap(dexOut, aContents); - bIndexMap = new IndexMap(dexOut, bContents); - aInstructionTransformer = new InstructionTransformer(aIndexMap); - bInstructionTransformer = new InstructionTransformer(bIndexMap); + TableOfContents aContents = dexA.getTableOfContents(); + TableOfContents bContents = dexB.getTableOfContents(); + aIndexMap = new IndexMap(dexOut, aContents); + bIndexMap = new IndexMap(dexOut, bContents); + aInstructionTransformer = new InstructionTransformer(aIndexMap); + bInstructionTransformer = new InstructionTransformer(bIndexMap); - headerOut = dexOut.appendSection(writerSizes.header, "header"); - idsDefsOut = dexOut.appendSection(writerSizes.idsDefs, "ids defs"); + headerOut = dexOut.appendSection(writerSizes.header, "header"); + idsDefsOut = dexOut.appendSection(writerSizes.idsDefs, "ids defs"); - contentsOut = dexOut.getTableOfContents(); - contentsOut.dataOff = dexOut.getLength(); + contentsOut = dexOut.getTableOfContents(); + contentsOut.dataOff = dexOut.getLength(); - contentsOut.mapList.off = dexOut.getLength(); - contentsOut.mapList.size = 1; - mapListOut = dexOut.appendSection(writerSizes.mapList, "map list"); + contentsOut.mapList.off = dexOut.getLength(); + contentsOut.mapList.size = 1; + mapListOut = dexOut.appendSection(writerSizes.mapList, "map list"); - contentsOut.typeLists.off = dexOut.getLength(); - typeListOut = dexOut.appendSection(writerSizes.typeList, "type list"); + contentsOut.typeLists.off = dexOut.getLength(); + typeListOut = dexOut.appendSection(writerSizes.typeList, "type list"); - contentsOut.annotationSetRefLists.off = dexOut.getLength(); - annotationSetRefListOut = dexOut.appendSection( - writerSizes.annotationsSetRefList, "annotation set ref list"); + contentsOut.annotationSetRefLists.off = dexOut.getLength(); + annotationSetRefListOut = + dexOut.appendSection(writerSizes.annotationsSetRefList, "annotation set ref list"); - contentsOut.annotationSets.off = dexOut.getLength(); - annotationSetOut = dexOut.appendSection(writerSizes.annotationsSet, "annotation sets"); + contentsOut.annotationSets.off = dexOut.getLength(); + annotationSetOut = dexOut.appendSection(writerSizes.annotationsSet, "annotation sets"); - contentsOut.classDatas.off = dexOut.getLength(); - classDataOut = dexOut.appendSection(writerSizes.classData, "class data"); + contentsOut.classDatas.off = dexOut.getLength(); + classDataOut = dexOut.appendSection(writerSizes.classData, "class data"); - contentsOut.codes.off = dexOut.getLength(); - codeOut = dexOut.appendSection(writerSizes.code, "code"); + contentsOut.codes.off = dexOut.getLength(); + codeOut = dexOut.appendSection(writerSizes.code, "code"); - contentsOut.stringDatas.off = dexOut.getLength(); - stringDataOut = dexOut.appendSection(writerSizes.stringData, "string data"); + contentsOut.stringDatas.off = dexOut.getLength(); + stringDataOut = dexOut.appendSection(writerSizes.stringData, "string data"); - contentsOut.debugInfos.off = dexOut.getLength(); - debugInfoOut = dexOut.appendSection(writerSizes.debugInfo, "debug info"); + contentsOut.debugInfos.off = dexOut.getLength(); + debugInfoOut = dexOut.appendSection(writerSizes.debugInfo, "debug info"); - contentsOut.annotations.off = dexOut.getLength(); - annotationOut = dexOut.appendSection(writerSizes.annotation, "annotation"); + contentsOut.annotations.off = dexOut.getLength(); + annotationOut = dexOut.appendSection(writerSizes.annotation, "annotation"); - contentsOut.encodedArrays.off = dexOut.getLength(); - encodedArrayOut = dexOut.appendSection(writerSizes.encodedArray, "encoded array"); + contentsOut.encodedArrays.off = dexOut.getLength(); + encodedArrayOut = dexOut.appendSection(writerSizes.encodedArray, "encoded array"); - contentsOut.annotationsDirectories.off = dexOut.getLength(); - annotationsDirectoryOut = dexOut.appendSection( - writerSizes.annotationsDirectory, "annotations directory"); + contentsOut.annotationsDirectories.off = dexOut.getLength(); + annotationsDirectoryOut = + dexOut.appendSection(writerSizes.annotationsDirectory, "annotations directory"); - dexOut.noMoreSections(); - contentsOut.dataSize = dexOut.getLength() - contentsOut.dataOff; - } + dexOut.noMoreSections(); + contentsOut.dataSize = dexOut.getLength() - contentsOut.dataOff; + } - public void setCompactWasteThreshold(int compactWasteThreshold) { - this.compactWasteThreshold = compactWasteThreshold; - } + public void setCompactWasteThreshold(int compactWasteThreshold) { + this.compactWasteThreshold = compactWasteThreshold; + } - private DexBuffer mergeDexBuffers() throws IOException { - mergeStringIds(); - mergeTypeIds(); - mergeTypeLists(); - mergeProtoIds(); - mergeFieldIds(); - mergeMethodIds(); - mergeAnnotations(); - unionAnnotationSetsAndDirectories(); - mergeClassDefs(); - - // write the header - contentsOut.header.off = 0; - contentsOut.header.size = 1; - contentsOut.fileSize = dexOut.getLength(); - contentsOut.computeSizesFromOffsets(); - contentsOut.writeHeader(headerOut); - contentsOut.writeMap(mapListOut); - - // generate and write the hashes - new DexHasher().writeHashes(dexOut); - - return dexOut; - } + private DexBuffer mergeDexBuffers() throws IOException { + mergeStringIds(); + mergeTypeIds(); + mergeTypeLists(); + mergeProtoIds(); + mergeFieldIds(); + mergeMethodIds(); + mergeAnnotations(); + unionAnnotationSetsAndDirectories(); + mergeClassDefs(); - public DexBuffer merge() throws IOException { - long start = System.nanoTime(); - DexBuffer result = mergeDexBuffers(); - - /* - * We use pessimistic sizes when merging dex files. If those sizes - * result in too many bytes wasted, compact the result. To compact, - * simply merge the result with itself. - */ - WriterSizes compactedSizes = new WriterSizes(this); - int wastedByteCount = writerSizes.size() - compactedSizes.size(); - if (wastedByteCount > + compactWasteThreshold) { - DexMerger compacter = new DexMerger( - dexOut, new DexBuffer(), CollisionPolicy.FAIL, compactedSizes); - result = compacter.mergeDexBuffers(); - System.out.printf("Result compacted from %.1fKiB to %.1fKiB to save %.1fKiB%n", - dexOut.getLength() / 1024f, - result.getLength() / 1024f, - wastedByteCount / 1024f); - } + // write the header + contentsOut.header.off = 0; + contentsOut.header.size = 1; + contentsOut.fileSize = dexOut.getLength(); + contentsOut.computeSizesFromOffsets(); + contentsOut.writeHeader(headerOut); + contentsOut.writeMap(mapListOut); + + // generate and write the hashes + new DexHasher().writeHashes(dexOut); + + return dexOut; + } + + public DexBuffer merge() throws IOException { + long start = System.nanoTime(); + DexBuffer result = mergeDexBuffers(); + + /* + * We use pessimistic sizes when merging dex files. If those sizes + * result in too many bytes wasted, compact the result. To compact, + * simply merge the result with itself. + */ + WriterSizes compactedSizes = new WriterSizes(this); + int wastedByteCount = writerSizes.size() - compactedSizes.size(); + if (wastedByteCount > +compactWasteThreshold) { + DexMerger compacter = + new DexMerger(dexOut, new DexBuffer(), CollisionPolicy.FAIL, compactedSizes); + result = compacter.mergeDexBuffers(); + System.out.printf("Result compacted from %.1fKiB to %.1fKiB to save %.1fKiB%n", + dexOut.getLength() / 1024f, result.getLength() / 1024f, wastedByteCount / 1024f); + } - long elapsed = System.nanoTime() - start; - System.out.printf("Merged dex A (%d defs/%.1fKiB) with dex B " - + "(%d defs/%.1fKiB). Result is %d defs/%.1fKiB. Took %.1fs%n", - dexA.getTableOfContents().classDefs.size, - dexA.getLength() / 1024f, - dexB.getTableOfContents().classDefs.size, - dexB.getLength() / 1024f, - result.getTableOfContents().classDefs.size, - result.getLength() / 1024f, - elapsed / 1000000000f); - - return result; + long elapsed = System.nanoTime() - start; + System.out.printf("Merged dex A (%d defs/%.1fKiB) with dex B " + + "(%d defs/%.1fKiB). Result is %d defs/%.1fKiB. Took %.1fs%n", + dexA.getTableOfContents().classDefs.size, + dexA.getLength() / 1024f, + dexB.getTableOfContents().classDefs.size, + dexB.getLength() / 1024f, + result.getTableOfContents().classDefs.size, + result.getLength() / 1024f, + elapsed / 1000000000f); + + return result; + } + + /** + * Reads an IDs section of two dex files and writes an IDs section of a + * merged dex file. Populates maps from old to new indices in the process. + */ + abstract class IdMerger<T extends Comparable<T>> { + private final DexBuffer.Section out; + + protected IdMerger(DexBuffer.Section out) { + this.out = out; } /** - * Reads an IDs section of two dex files and writes an IDs section of a - * merged dex file. Populates maps from old to new indices in the process. + * Merges already-sorted sections, reading only two values into memory + * at a time. */ - abstract class IdMerger<T extends Comparable<T>> { - private final DexBuffer.Section out; - - protected IdMerger(DexBuffer.Section out) { - this.out = out; + public final void mergeSorted() { + TableOfContents.Section aSection = getSection(dexA.getTableOfContents()); + TableOfContents.Section bSection = getSection(dexB.getTableOfContents()); + getSection(contentsOut).off = out.getPosition(); + + DexBuffer.Section inA = aSection.exists() ? dexA.open(aSection.off) : null; + DexBuffer.Section inB = bSection.exists() ? dexB.open(bSection.off) : null; + int aOffset = -1; + int bOffset = -1; + int aIndex = 0; + int bIndex = 0; + int outCount = 0; + T a = null; + T b = null; + + while (true) { + if (a == null && aIndex < aSection.size) { + aOffset = inA.getPosition(); + a = read(inA, aIndexMap, aIndex); } - - /** - * Merges already-sorted sections, reading only two values into memory - * at a time. - */ - public final void mergeSorted() { - TableOfContents.Section aSection = getSection(dexA.getTableOfContents()); - TableOfContents.Section bSection = getSection(dexB.getTableOfContents()); - getSection(contentsOut).off = out.getPosition(); - - DexBuffer.Section inA = aSection.exists() ? dexA.open(aSection.off) : null; - DexBuffer.Section inB = bSection.exists() ? dexB.open(bSection.off) : null; - int aOffset = -1; - int bOffset = -1; - int aIndex = 0; - int bIndex = 0; - int outCount = 0; - T a = null; - T b = null; - - while (true) { - if (a == null && aIndex < aSection.size) { - aOffset = inA.getPosition(); - a = read(inA, aIndexMap, aIndex); - } - if (b == null && bIndex < bSection.size) { - bOffset = inB.getPosition(); - b = read(inB, bIndexMap, bIndex); - } - - // Write the smaller of a and b. If they're equal, write only once - boolean advanceA; - boolean advanceB; - if (a != null && b != null) { - int compare = a.compareTo(b); - advanceA = compare <= 0; - advanceB = compare >= 0; - } else { - advanceA = (a != null); - advanceB = (b != null); - } - - T toWrite = null; - if (advanceA) { - toWrite = a; - updateIndex(aOffset, aIndexMap, aIndex++, outCount); - a = null; - aOffset = -1; - } - if (advanceB) { - toWrite = b; - updateIndex(bOffset, bIndexMap, bIndex++, outCount); - b = null; - bOffset = -1; - } - if (toWrite == null) { - break; // advanceA == false && advanceB == false - } - write(toWrite); - outCount++; - } - - getSection(contentsOut).size = outCount; + if (b == null && bIndex < bSection.size) { + bOffset = inB.getPosition(); + b = read(inB, bIndexMap, bIndex); } - /** - * Merges unsorted sections by reading them completely into memory and - * sorting in memory. - */ - public final void mergeUnsorted() { - getSection(contentsOut).off = out.getPosition(); - - List<UnsortedValue> all = new ArrayList<UnsortedValue>(); - all.addAll(readUnsortedValues(dexA, aIndexMap)); - all.addAll(readUnsortedValues(dexB, bIndexMap)); - Collections.sort(all); - - int outCount = 0; - for (int i = 0; i < all.size(); ) { - UnsortedValue e1 = all.get(i++); - updateIndex(e1.offset, getIndexMap(e1.source), e1.index, outCount - 1); - - while (i < all.size() && e1.compareTo(all.get(i)) == 0) { - UnsortedValue e2 = all.get(i++); - updateIndex(e2.offset, getIndexMap(e2.source), e2.index, outCount - 1); - } - - write(e1.value); - outCount++; - } - - getSection(contentsOut).size = outCount; + // Write the smaller of a and b. If they're equal, write only once + boolean advanceA; + boolean advanceB; + if (a != null && b != null) { + int compare = a.compareTo(b); + advanceA = compare <= 0; + advanceB = compare >= 0; + } else { + advanceA = (a != null); + advanceB = (b != null); } - private List<UnsortedValue> readUnsortedValues(DexBuffer source, IndexMap indexMap) { - TableOfContents.Section section = getSection(source.getTableOfContents()); - if (!section.exists()) { - return Collections.emptyList(); - } - - List<UnsortedValue> result = new ArrayList<UnsortedValue>(); - DexBuffer.Section in = source.open(section.off); - for (int i = 0; i < section.size; i++) { - int offset = in.getPosition(); - T value = read(in, indexMap, 0); - result.add(new UnsortedValue(source, indexMap, value, i, offset)); - } - return result; + T toWrite = null; + if (advanceA) { + toWrite = a; + updateIndex(aOffset, aIndexMap, aIndex++, outCount); + a = null; + aOffset = -1; } - - abstract TableOfContents.Section getSection(TableOfContents tableOfContents); - abstract T read(DexBuffer.Section in, IndexMap indexMap, int index); - abstract void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex); - abstract void write(T value); - - class UnsortedValue implements Comparable<UnsortedValue> { - final DexBuffer source; - final IndexMap indexMap; - final T value; - final int index; - final int offset; - - UnsortedValue(DexBuffer source, IndexMap indexMap, T value, int index, int offset) { - this.source = source; - this.indexMap = indexMap; - this.value = value; - this.index = index; - this.offset = offset; - } - - public int compareTo(UnsortedValue unsortedValue) { - return value.compareTo(unsortedValue.value); - } + if (advanceB) { + toWrite = b; + updateIndex(bOffset, bIndexMap, bIndex++, outCount); + b = null; + bOffset = -1; } - } - - private IndexMap getIndexMap(DexBuffer dexBuffer) { - if (dexBuffer == dexA) { - return aIndexMap; - } else if (dexBuffer == dexB) { - return bIndexMap; - } else { - throw new IllegalArgumentException(); + if (toWrite == null) { + break; // advanceA == false && advanceB == false } - } - - private void mergeStringIds() { - new IdMerger<String>(idsDefsOut) { - @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { - return tableOfContents.stringIds; - } - - @Override String read(DexBuffer.Section in, IndexMap indexMap, int index) { - return in.readString(); - } - - @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { - indexMap.stringIds[oldIndex] = newIndex; - } - - @Override void write(String value) { - contentsOut.stringDatas.size++; - idsDefsOut.writeInt(stringDataOut.getPosition()); - stringDataOut.writeStringData(value); - } - }.mergeSorted(); - } + write(toWrite); + outCount++; + } - private void mergeTypeIds() { - new IdMerger<Integer>(idsDefsOut) { - @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { - return tableOfContents.typeIds; - } - - @Override Integer read(DexBuffer.Section in, IndexMap indexMap, int index) { - int stringIndex = in.readInt(); - return indexMap.adjustString(stringIndex); - } - - @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { - checkIndex16(newIndex); - indexMap.typeIds[oldIndex] = (short) newIndex; - } - - @Override void write(Integer value) { - idsDefsOut.writeInt(value); - } - }.mergeSorted(); + getSection(contentsOut).size = outCount; } - private void mergeTypeLists() { - new IdMerger<TypeList>(typeListOut) { - @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { - return tableOfContents.typeLists; - } - - @Override TypeList read(DexBuffer.Section in, IndexMap indexMap, int index) { - return indexMap.adjustTypeList(in.readTypeList()); - } - - @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { - indexMap.putTypeListOffset(offset, typeListOut.getPosition()); - } + /** + * Merges unsorted sections by reading them completely into memory and + * sorting in memory. + */ + public final void mergeUnsorted() { + getSection(contentsOut).off = out.getPosition(); + + List<UnsortedValue> all = new ArrayList<UnsortedValue>(); + all.addAll(readUnsortedValues(dexA, aIndexMap)); + all.addAll(readUnsortedValues(dexB, bIndexMap)); + Collections.sort(all); + + int outCount = 0; + for (int i = 0; i < all.size();) { + UnsortedValue e1 = all.get(i++); + updateIndex(e1.offset, getIndexMap(e1.source), e1.index, outCount - 1); + + while (i < all.size() && e1.compareTo(all.get(i)) == 0) { + UnsortedValue e2 = all.get(i++); + updateIndex(e2.offset, getIndexMap(e2.source), e2.index, outCount - 1); + } - @Override void write(TypeList value) { - typeListOut.writeTypeList(value); - } - }.mergeUnsorted(); - } + write(e1.value); + outCount++; + } - private void mergeProtoIds() { - new IdMerger<ProtoId>(idsDefsOut) { - @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { - return tableOfContents.protoIds; - } - - @Override ProtoId read(DexBuffer.Section in, IndexMap indexMap, int index) { - return indexMap.adjust(in.readProtoId()); - } - - @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { - checkIndex16(newIndex); - indexMap.protoIds[oldIndex] = (short) newIndex; - } - - @Override void write(ProtoId value) { - value.writeTo(idsDefsOut); - } - }.mergeSorted(); + getSection(contentsOut).size = outCount; } - private void mergeFieldIds() { - new IdMerger<FieldId>(idsDefsOut) { - @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { - return tableOfContents.fieldIds; - } - - @Override FieldId read(DexBuffer.Section in, IndexMap indexMap, int index) { - return indexMap.adjust(in.readFieldId()); - } - - @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { - checkIndex16(newIndex); - indexMap.fieldIds[oldIndex] = (short) newIndex; - } - - @Override void write(FieldId value) { - value.writeTo(idsDefsOut); - } - }.mergeSorted(); + private List<UnsortedValue> readUnsortedValues(DexBuffer source, IndexMap indexMap) { + TableOfContents.Section section = getSection(source.getTableOfContents()); + if (!section.exists()) { + return Collections.emptyList(); + } + + List<UnsortedValue> result = new ArrayList<UnsortedValue>(); + DexBuffer.Section in = source.open(section.off); + for (int i = 0; i < section.size; i++) { + int offset = in.getPosition(); + T value = read(in, indexMap, 0); + result.add(new UnsortedValue(source, indexMap, value, i, offset)); + } + return result; } - private void mergeMethodIds() { - new IdMerger<MethodId>(idsDefsOut) { - @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { - return tableOfContents.methodIds; - } - - @Override MethodId read(DexBuffer.Section in, IndexMap indexMap, int index) { - return indexMap.adjust(in.readMethodId()); - } - - @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { - checkIndex16(newIndex); - indexMap.methodIds[oldIndex] = (short) newIndex; - } - - @Override void write(MethodId methodId) { - methodId.writeTo(idsDefsOut); - } - }.mergeSorted(); - } + abstract TableOfContents.Section getSection(TableOfContents tableOfContents); - private void mergeAnnotations() { - new IdMerger<Annotation>(annotationOut) { - @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { - return tableOfContents.annotations; - } + abstract T read(DexBuffer.Section in, IndexMap indexMap, int index); - @Override Annotation read(DexBuffer.Section in, IndexMap indexMap, int index) { - return indexMap.adjust(in.readAnnotation()); - } + abstract void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex); - @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { - indexMap.putAnnotationOffset(offset, annotationOut.getPosition()); - } + abstract void write(T value); - @Override void write(Annotation value) { - value.writeTo(annotationOut); - } - }.mergeUnsorted(); - } + class UnsortedValue implements Comparable<UnsortedValue> { + final DexBuffer source; + final IndexMap indexMap; + final T value; + final int index; + final int offset; - private void mergeClassDefs() { - SortableType[] types = getSortedTypes(); - contentsOut.classDefs.off = idsDefsOut.getPosition(); - contentsOut.classDefs.size = types.length; + UnsortedValue(DexBuffer source, IndexMap indexMap, T value, int index, int offset) { + this.source = source; + this.indexMap = indexMap; + this.value = value; + this.index = index; + this.offset = offset; + } - for (SortableType type : types) { - DexBuffer in = type.getBuffer(); - IndexMap indexMap = (in == dexA) ? aIndexMap : bIndexMap; - transformClassDef(in, type.getClassDef(), indexMap); - } + @Override + public int compareTo(UnsortedValue unsortedValue) { + return value.compareTo(unsortedValue.value); + } } - - /** - * Returns the union of classes from both files, sorted in order such that - * a class is always preceded by its supertype and implemented interfaces. - */ - private SortableType[] getSortedTypes() { - // size is pessimistic; doesn't include arrays - SortableType[] sortableTypes = new SortableType[contentsOut.typeIds.size]; - readSortableTypes(sortableTypes, dexA, aIndexMap); - readSortableTypes(sortableTypes, dexB, bIndexMap); - - /* - * Populate the depths of each sortable type. This makes D iterations - * through all N types, where 'D' is the depth of the deepest type. For - * example, the deepest class in libcore is Xalan's KeyIterator, which - * is 11 types deep. - */ - while (true) { - boolean allDone = true; - for (SortableType sortableType : sortableTypes) { - if (sortableType != null && !sortableType.isDepthAssigned()) { - allDone &= sortableType.tryAssignDepth(sortableTypes); - } - } - if (allDone) { - break; - } - } - - // Now that all types have depth information, the result can be sorted - Arrays.sort(sortableTypes, SortableType.NULLS_LAST_ORDER); - - // Strip nulls from the end - int firstNull = Arrays.asList(sortableTypes).indexOf(null); - return firstNull != -1 - ? Arrays.copyOfRange(sortableTypes, 0, firstNull) - : sortableTypes; + } + + private IndexMap getIndexMap(DexBuffer dexBuffer) { + if (dexBuffer == dexA) { + return aIndexMap; + } else if (dexBuffer == dexB) { + return bIndexMap; + } else { + throw new IllegalArgumentException(); } - - /** - * Reads just enough data on each class so that we can sort it and then find - * it later. + } + + private void mergeStringIds() { + new IdMerger<String>(idsDefsOut) { + @Override + TableOfContents.Section getSection(TableOfContents tableOfContents) { + return tableOfContents.stringIds; + } + + @Override + String read(DexBuffer.Section in, IndexMap indexMap, int index) { + return in.readString(); + } + + @Override + void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { + indexMap.stringIds[oldIndex] = newIndex; + } + + @Override + void write(String value) { + contentsOut.stringDatas.size++; + idsDefsOut.writeInt(stringDataOut.getPosition()); + stringDataOut.writeStringData(value); + } + }.mergeSorted(); + } + + private void mergeTypeIds() { + new IdMerger<Integer>(idsDefsOut) { + @Override + TableOfContents.Section getSection(TableOfContents tableOfContents) { + return tableOfContents.typeIds; + } + + @Override + Integer read(DexBuffer.Section in, IndexMap indexMap, int index) { + int stringIndex = in.readInt(); + return indexMap.adjustString(stringIndex); + } + + @Override + void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { + checkIndex16(newIndex); + indexMap.typeIds[oldIndex] = (short) newIndex; + } + + @Override + void write(Integer value) { + idsDefsOut.writeInt(value); + } + }.mergeSorted(); + } + + private void mergeTypeLists() { + new IdMerger<TypeList>(typeListOut) { + @Override + TableOfContents.Section getSection(TableOfContents tableOfContents) { + return tableOfContents.typeLists; + } + + @Override + TypeList read(DexBuffer.Section in, IndexMap indexMap, int index) { + return indexMap.adjustTypeList(in.readTypeList()); + } + + @Override + void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { + indexMap.putTypeListOffset(offset, typeListOut.getPosition()); + } + + @Override + void write(TypeList value) { + typeListOut.writeTypeList(value); + } + }.mergeUnsorted(); + } + + private void mergeProtoIds() { + new IdMerger<ProtoId>(idsDefsOut) { + @Override + TableOfContents.Section getSection(TableOfContents tableOfContents) { + return tableOfContents.protoIds; + } + + @Override + ProtoId read(DexBuffer.Section in, IndexMap indexMap, int index) { + return indexMap.adjust(in.readProtoId()); + } + + @Override + void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { + checkIndex16(newIndex); + indexMap.protoIds[oldIndex] = (short) newIndex; + } + + @Override + void write(ProtoId value) { + value.writeTo(idsDefsOut); + } + }.mergeSorted(); + } + + private void mergeFieldIds() { + new IdMerger<FieldId>(idsDefsOut) { + @Override + TableOfContents.Section getSection(TableOfContents tableOfContents) { + return tableOfContents.fieldIds; + } + + @Override + FieldId read(DexBuffer.Section in, IndexMap indexMap, int index) { + return indexMap.adjust(in.readFieldId()); + } + + @Override + void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { + checkIndex16(newIndex); + indexMap.fieldIds[oldIndex] = (short) newIndex; + } + + @Override + void write(FieldId value) { + value.writeTo(idsDefsOut); + } + }.mergeSorted(); + } + + private void mergeMethodIds() { + new IdMerger<MethodId>(idsDefsOut) { + @Override + TableOfContents.Section getSection(TableOfContents tableOfContents) { + return tableOfContents.methodIds; + } + + @Override + MethodId read(DexBuffer.Section in, IndexMap indexMap, int index) { + return indexMap.adjust(in.readMethodId()); + } + + @Override + void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { + checkIndex16(newIndex); + indexMap.methodIds[oldIndex] = (short) newIndex; + } + + @Override + void write(MethodId methodId) { + methodId.writeTo(idsDefsOut); + } + }.mergeSorted(); + } + + private void mergeAnnotations() { + new IdMerger<Annotation>(annotationOut) { + @Override + TableOfContents.Section getSection(TableOfContents tableOfContents) { + return tableOfContents.annotations; + } + + @Override + Annotation read(DexBuffer.Section in, IndexMap indexMap, int index) { + return indexMap.adjust(in.readAnnotation()); + } + + @Override + void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { + indexMap.putAnnotationOffset(offset, annotationOut.getPosition()); + } + + @Override + void write(Annotation value) { + value.writeTo(annotationOut); + } + }.mergeUnsorted(); + } + + private void mergeClassDefs() { + SortableType[] types = getSortedTypes(); + contentsOut.classDefs.off = idsDefsOut.getPosition(); + contentsOut.classDefs.size = types.length; + + for (SortableType type : types) { + DexBuffer in = type.getBuffer(); + IndexMap indexMap = (in == dexA) ? aIndexMap : bIndexMap; + transformClassDef(in, type.getClassDef(), indexMap); + } + } + + /** + * Returns the union of classes from both files, sorted in order such that + * a class is always preceded by its supertype and implemented interfaces. + */ + private SortableType[] getSortedTypes() { + // size is pessimistic; doesn't include arrays + SortableType[] sortableTypes = new SortableType[contentsOut.typeIds.size]; + readSortableTypes(sortableTypes, dexA, aIndexMap); + readSortableTypes(sortableTypes, dexB, bIndexMap); + + /* + * Populate the depths of each sortable type. This makes D iterations + * through all N types, where 'D' is the depth of the deepest type. For + * example, the deepest class in libcore is Xalan's KeyIterator, which + * is 11 types deep. */ - private void readSortableTypes(SortableType[] sortableTypes, DexBuffer buffer, - IndexMap indexMap) { - for (ClassDef classDef : buffer.classDefs()) { - SortableType sortableType = indexMap.adjust(new SortableType(buffer, classDef)); - int t = sortableType.getTypeIndex(); - if (sortableTypes[t] == null) { - sortableTypes[t] = sortableType; - } else if (collisionPolicy != CollisionPolicy.KEEP_FIRST) { - throw new DexException("Multiple dex files define " - + buffer.typeNames().get(classDef.getTypeIndex())); - } + while (true) { + boolean allDone = true; + for (SortableType sortableType : sortableTypes) { + if (sortableType != null && !sortableType.isDepthAssigned()) { + allDone &= sortableType.tryAssignDepth(sortableTypes); } + } + if (allDone) { + break; + } } - /** - * Copy annotation sets from each input to the output. - * - * TODO: this may write multiple copies of the same annotation set. - * We should shrink the output by merging rather than unioning - */ - private void unionAnnotationSetsAndDirectories() { - transformAnnotationSets(dexA, aIndexMap); - transformAnnotationSets(dexB, bIndexMap); - transformAnnotationDirectories(dexA, aIndexMap); - transformAnnotationDirectories(dexB, bIndexMap); - transformStaticValues(dexA, aIndexMap); - transformStaticValues(dexB, bIndexMap); + // Now that all types have depth information, the result can be sorted + Arrays.sort(sortableTypes, SortableType.NULLS_LAST_ORDER); + + // Strip nulls from the end + int firstNull = Arrays.asList(sortableTypes).indexOf(null); + return firstNull != -1 ? Arrays.copyOfRange(sortableTypes, 0, firstNull) : sortableTypes; + } + + /** + * Reads just enough data on each class so that we can sort it and then find + * it later. + */ + private void readSortableTypes(SortableType[] sortableTypes, DexBuffer buffer, + IndexMap indexMap) { + for (ClassDef classDef : buffer.classDefs()) { + SortableType sortableType = indexMap.adjust(new SortableType(buffer, classDef)); + int t = sortableType.getTypeIndex(); + if (sortableTypes[t] == null) { + sortableTypes[t] = sortableType; + } else if (collisionPolicy != CollisionPolicy.KEEP_FIRST) { + throw new DexException( + "Multiple dex files define " + buffer.typeNames().get(classDef.getTypeIndex())); + } } - - private void transformAnnotationSets(DexBuffer in, IndexMap indexMap) { - TableOfContents.Section section = in.getTableOfContents().annotationSets; - if (section.exists()) { - DexBuffer.Section setIn = in.open(section.off); - for (int i = 0; i < section.size; i++) { - transformAnnotationSet(indexMap, setIn); - } - } + } + + /** + * Copy annotation sets from each input to the output. + * + * TODO(dx team): this may write multiple copies of the same annotation set. + * We should shrink the output by merging rather than unioning + */ + private void unionAnnotationSetsAndDirectories() { + transformAnnotationSets(dexA, aIndexMap); + transformAnnotationSets(dexB, bIndexMap); + transformAnnotationDirectories(dexA, aIndexMap); + transformAnnotationDirectories(dexB, bIndexMap); + transformStaticValues(dexA, aIndexMap); + transformStaticValues(dexB, bIndexMap); + } + + private void transformAnnotationSets(DexBuffer in, IndexMap indexMap) { + TableOfContents.Section section = in.getTableOfContents().annotationSets; + if (section.exists()) { + DexBuffer.Section setIn = in.open(section.off); + for (int i = 0; i < section.size; i++) { + transformAnnotationSet(indexMap, setIn); + } } + } - private void checkIndex16(int index) { - if (index > Character.MAX_VALUE || index < 0) { - throw new DexException("Too many IDs in dex"); - } + private void checkIndex16(int index) { + if (index > Character.MAX_VALUE || index < 0) { + throw new DexException("Too many IDs in dex"); } - - private void transformAnnotationDirectories(DexBuffer in, IndexMap indexMap) { - TableOfContents.Section section = in.getTableOfContents().annotationsDirectories; - if (section.exists()) { - DexBuffer.Section directoryIn = in.open(section.off); - for (int i = 0; i < section.size; i++) { - transformAnnotationDirectory(in, directoryIn, indexMap); - } - } + } + + private void transformAnnotationDirectories(DexBuffer in, IndexMap indexMap) { + TableOfContents.Section section = in.getTableOfContents().annotationsDirectories; + if (section.exists()) { + DexBuffer.Section directoryIn = in.open(section.off); + for (int i = 0; i < section.size; i++) { + transformAnnotationDirectory(in, directoryIn, indexMap); + } } - - private void transformStaticValues(DexBuffer in, IndexMap indexMap) { - TableOfContents.Section section = in.getTableOfContents().encodedArrays; - if (section.exists()) { - DexBuffer.Section staticValuesIn = in.open(section.off); - for (int i = 0; i < section.size; i++) { - transformStaticValues(staticValuesIn, indexMap); - } - } + } + + private void transformStaticValues(DexBuffer in, IndexMap indexMap) { + TableOfContents.Section section = in.getTableOfContents().encodedArrays; + if (section.exists()) { + DexBuffer.Section staticValuesIn = in.open(section.off); + for (int i = 0; i < section.size; i++) { + transformStaticValues(staticValuesIn, indexMap); + } } - - /** - * Reads a class_def_item beginning at {@code in} and writes the index and - * data. - */ - private void transformClassDef(DexBuffer in, ClassDef classDef, IndexMap indexMap) { - idsDefsOut.assertFourByteAligned(); - idsDefsOut.writeInt(classDef.getTypeIndex()); - idsDefsOut.writeInt(classDef.getAccessFlags()); - idsDefsOut.writeInt(classDef.getSupertypeIndex()); - idsDefsOut.writeInt(classDef.getInterfacesOffset()); - - int sourceFileIndex = indexMap.adjustString(classDef.getSourceFileIndex()); - idsDefsOut.writeInt(sourceFileIndex); - - int annotationsOff = classDef.getAnnotationsOffset(); - idsDefsOut.writeInt(indexMap.adjustAnnotationDirectory(annotationsOff)); - - int classDataOff = classDef.getClassDataOffset(); - if (classDataOff == 0) { - idsDefsOut.writeInt(0); - } else { - idsDefsOut.writeInt(classDataOut.getPosition()); - ClassData classData = in.readClassData(classDef); - transformClassData(in, classData, indexMap); - } - - int staticValuesOff = classDef.getStaticValuesOffset(); - idsDefsOut.writeInt(indexMap.adjustStaticValues(staticValuesOff)); + } + + /** + * Reads a class_def_item beginning at {@code in} and writes the index and + * data. + */ + private void transformClassDef(DexBuffer in, ClassDef classDef, IndexMap indexMap) { + idsDefsOut.assertFourByteAligned(); + idsDefsOut.writeInt(classDef.getTypeIndex()); + idsDefsOut.writeInt(classDef.getAccessFlags()); + idsDefsOut.writeInt(classDef.getSupertypeIndex()); + idsDefsOut.writeInt(classDef.getInterfacesOffset()); + + int sourceFileIndex = indexMap.adjustString(classDef.getSourceFileIndex()); + idsDefsOut.writeInt(sourceFileIndex); + + int annotationsOff = classDef.getAnnotationsOffset(); + idsDefsOut.writeInt(indexMap.adjustAnnotationDirectory(annotationsOff)); + + int classDataOff = classDef.getClassDataOffset(); + if (classDataOff == 0) { + idsDefsOut.writeInt(0); + } else { + idsDefsOut.writeInt(classDataOut.getPosition()); + ClassData classData = in.readClassData(classDef); + transformClassData(in, classData, indexMap); } - /** - * Transform all annotations on a class. - */ - private void transformAnnotationDirectory( - DexBuffer in, DexBuffer.Section directoryIn, IndexMap indexMap) { - contentsOut.annotationsDirectories.size++; - annotationsDirectoryOut.assertFourByteAligned(); - indexMap.putAnnotationDirectoryOffset( - directoryIn.getPosition(), annotationsDirectoryOut.getPosition()); - - int classAnnotationsOffset = indexMap.adjustAnnotationSet(directoryIn.readInt()); - annotationsDirectoryOut.writeInt(classAnnotationsOffset); + int staticValuesOff = classDef.getStaticValuesOffset(); + idsDefsOut.writeInt(indexMap.adjustStaticValues(staticValuesOff)); + } - int fieldsSize = directoryIn.readInt(); - annotationsDirectoryOut.writeInt(fieldsSize); + /** + * Transform all annotations on a class. + */ + private void transformAnnotationDirectory(DexBuffer in, DexBuffer.Section directoryIn, + IndexMap indexMap) { + contentsOut.annotationsDirectories.size++; + annotationsDirectoryOut.assertFourByteAligned(); + indexMap.putAnnotationDirectoryOffset(directoryIn.getPosition(), + annotationsDirectoryOut.getPosition()); - int methodsSize = directoryIn.readInt(); - annotationsDirectoryOut.writeInt(methodsSize); + int classAnnotationsOffset = indexMap.adjustAnnotationSet(directoryIn.readInt()); + annotationsDirectoryOut.writeInt(classAnnotationsOffset); - int parameterListSize = directoryIn.readInt(); - annotationsDirectoryOut.writeInt(parameterListSize); + int fieldsSize = directoryIn.readInt(); + annotationsDirectoryOut.writeInt(fieldsSize); - for (int i = 0; i < fieldsSize; i++) { - // field index - annotationsDirectoryOut.writeInt(indexMap.adjustField(directoryIn.readInt())); + int methodsSize = directoryIn.readInt(); + annotationsDirectoryOut.writeInt(methodsSize); - // annotations offset - annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSet(directoryIn.readInt())); - } + int parameterListSize = directoryIn.readInt(); + annotationsDirectoryOut.writeInt(parameterListSize); - for (int i = 0; i < methodsSize; i++) { - // method index - annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt())); + for (int i = 0; i < fieldsSize; i++) { + // field index + annotationsDirectoryOut.writeInt(indexMap.adjustField(directoryIn.readInt())); - // annotation set offset - annotationsDirectoryOut.writeInt( - indexMap.adjustAnnotationSet(directoryIn.readInt())); - } - - for (int i = 0; i < parameterListSize; i++) { - contentsOut.annotationSetRefLists.size++; - annotationSetRefListOut.assertFourByteAligned(); - - // method index - annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt())); + // annotations offset + annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSet(directoryIn.readInt())); + } - // annotations offset - annotationsDirectoryOut.writeInt(annotationSetRefListOut.getPosition()); - DexBuffer.Section refListIn = in.open(directoryIn.readInt()); + for (int i = 0; i < methodsSize; i++) { + // method index + annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt())); - // parameters - int parameterCount = refListIn.readInt(); - annotationSetRefListOut.writeInt(parameterCount); - for (int p = 0; p < parameterCount; p++) { - annotationSetRefListOut.writeInt(indexMap.adjustAnnotationSet(refListIn.readInt())); - } - } + // annotation set offset + annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSet(directoryIn.readInt())); } - /** - * Transform all annotations on a single type, member or parameter. - */ - private void transformAnnotationSet(IndexMap indexMap, DexBuffer.Section setIn) { - contentsOut.annotationSets.size++; - annotationSetOut.assertFourByteAligned(); - indexMap.putAnnotationSetOffset(setIn.getPosition(), annotationSetOut.getPosition()); + for (int i = 0; i < parameterListSize; i++) { + contentsOut.annotationSetRefLists.size++; + annotationSetRefListOut.assertFourByteAligned(); - int size = setIn.readInt(); - annotationSetOut.writeInt(size); + // method index + annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt())); - for (int j = 0; j < size; j++) { - annotationSetOut.writeInt(indexMap.adjustAnnotation(setIn.readInt())); - } - } + // annotations offset + annotationsDirectoryOut.writeInt(annotationSetRefListOut.getPosition()); + DexBuffer.Section refListIn = in.open(directoryIn.readInt()); - private void transformClassData(DexBuffer in, ClassData classData, IndexMap indexMap) { - contentsOut.classDatas.size++; + // parameters + int parameterCount = refListIn.readInt(); + annotationSetRefListOut.writeInt(parameterCount); + for (int p = 0; p < parameterCount; p++) { + annotationSetRefListOut.writeInt(indexMap.adjustAnnotationSet(refListIn.readInt())); + } + } + } - ClassData.Field[] staticFields = classData.getStaticFields(); - ClassData.Field[] instanceFields = classData.getInstanceFields(); - ClassData.Method[] directMethods = classData.getDirectMethods(); - ClassData.Method[] virtualMethods = classData.getVirtualMethods(); + /** + * Transform all annotations on a single type, member or parameter. + */ + private void transformAnnotationSet(IndexMap indexMap, DexBuffer.Section setIn) { + contentsOut.annotationSets.size++; + annotationSetOut.assertFourByteAligned(); + indexMap.putAnnotationSetOffset(setIn.getPosition(), annotationSetOut.getPosition()); - classDataOut.writeUleb128(staticFields.length); - classDataOut.writeUleb128(instanceFields.length); - classDataOut.writeUleb128(directMethods.length); - classDataOut.writeUleb128(virtualMethods.length); + int size = setIn.readInt(); + annotationSetOut.writeInt(size); - transformFields(indexMap, staticFields); - transformFields(indexMap, instanceFields); - transformMethods(in, indexMap, directMethods); - transformMethods(in, indexMap, virtualMethods); + for (int j = 0; j < size; j++) { + annotationSetOut.writeInt(indexMap.adjustAnnotation(setIn.readInt())); } - - private void transformFields(IndexMap indexMap, ClassData.Field[] fields) { - int lastOutFieldIndex = 0; - for (ClassData.Field field : fields) { - int outFieldIndex = indexMap.adjustField(field.getFieldIndex()); - classDataOut.writeUleb128(outFieldIndex - lastOutFieldIndex); - lastOutFieldIndex = outFieldIndex; - classDataOut.writeUleb128(field.getAccessFlags()); - } + } + + private void transformClassData(DexBuffer in, ClassData classData, IndexMap indexMap) { + contentsOut.classDatas.size++; + + ClassData.Field[] staticFields = classData.getStaticFields(); + ClassData.Field[] instanceFields = classData.getInstanceFields(); + ClassData.Method[] directMethods = classData.getDirectMethods(); + ClassData.Method[] virtualMethods = classData.getVirtualMethods(); + + classDataOut.writeUleb128(staticFields.length); + classDataOut.writeUleb128(instanceFields.length); + classDataOut.writeUleb128(directMethods.length); + classDataOut.writeUleb128(virtualMethods.length); + + transformFields(indexMap, staticFields); + transformFields(indexMap, instanceFields); + transformMethods(in, indexMap, directMethods); + transformMethods(in, indexMap, virtualMethods); + } + + private void transformFields(IndexMap indexMap, ClassData.Field[] fields) { + int lastOutFieldIndex = 0; + for (ClassData.Field field : fields) { + int outFieldIndex = indexMap.adjustField(field.getFieldIndex()); + classDataOut.writeUleb128(outFieldIndex - lastOutFieldIndex); + lastOutFieldIndex = outFieldIndex; + classDataOut.writeUleb128(field.getAccessFlags()); } - - private void transformMethods(DexBuffer in, IndexMap indexMap, ClassData.Method[] methods) { - int lastOutMethodIndex = 0; - for (ClassData.Method method : methods) { - int outMethodIndex = indexMap.adjustMethod(method.getMethodIndex()); - classDataOut.writeUleb128(outMethodIndex - lastOutMethodIndex); - lastOutMethodIndex = outMethodIndex; - - classDataOut.writeUleb128(method.getAccessFlags()); - - if (method.getCodeOffset() == 0) { - classDataOut.writeUleb128(0); - } else { - codeOut.alignToFourBytes(); - classDataOut.writeUleb128(codeOut.getPosition()); - transformCode(in, in.readCode(method), indexMap); - } - } + } + + private void transformMethods(DexBuffer in, IndexMap indexMap, ClassData.Method[] methods) { + int lastOutMethodIndex = 0; + for (ClassData.Method method : methods) { + int outMethodIndex = indexMap.adjustMethod(method.getMethodIndex()); + classDataOut.writeUleb128(outMethodIndex - lastOutMethodIndex); + lastOutMethodIndex = outMethodIndex; + + classDataOut.writeUleb128(method.getAccessFlags()); + + if (method.getCodeOffset() == 0) { + classDataOut.writeUleb128(0); + } else { + codeOut.alignToFourBytes(); + classDataOut.writeUleb128(codeOut.getPosition()); + transformCode(in, in.readCode(method), indexMap); + } } - - private void transformCode(DexBuffer in, Code code, IndexMap indexMap) { - contentsOut.codes.size++; - codeOut.assertFourByteAligned(); - - codeOut.writeUnsignedShort(code.getRegistersSize()); - codeOut.writeUnsignedShort(code.getInsSize()); - codeOut.writeUnsignedShort(code.getOutsSize()); - - Code.Try[] tries = code.getTries(); - Code.CatchHandler[] catchHandlers = code.getCatchHandlers(); - codeOut.writeUnsignedShort(tries.length); - - int debugInfoOffset = code.getDebugInfoOffset(); - if (debugInfoOffset != 0) { - codeOut.writeInt(debugInfoOut.getPosition()); - transformDebugInfoItem(in.open(debugInfoOffset), indexMap); - } else { - codeOut.writeInt(0); - } - - short[] instructions = code.getInstructions(); - InstructionTransformer transformer = (in == dexA) - ? aInstructionTransformer - : bInstructionTransformer; - short[] newInstructions = transformer.transform(instructions); - codeOut.writeInt(newInstructions.length); - codeOut.write(newInstructions); - - if (tries.length > 0) { - if (newInstructions.length % 2 == 1) { - codeOut.writeShort((short) 0); // padding - } - - /* - * We can't write the tries until we've written the catch handlers. - * Unfortunately they're in the opposite order in the dex file so we - * need to transform them out-of-order. - */ - DexBuffer.Section triesSection = dexOut.open(codeOut.getPosition()); - codeOut.skip(tries.length * SizeOf.TRY_ITEM); - int[] offsets = transformCatchHandlers(indexMap, catchHandlers); - transformTries(triesSection, tries, offsets); - } + } + + private void transformCode(DexBuffer in, Code code, IndexMap indexMap) { + contentsOut.codes.size++; + codeOut.assertFourByteAligned(); + + codeOut.writeUnsignedShort(code.getRegistersSize()); + codeOut.writeUnsignedShort(code.getInsSize()); + codeOut.writeUnsignedShort(code.getOutsSize()); + + Code.Try[] tries = code.getTries(); + Code.CatchHandler[] catchHandlers = code.getCatchHandlers(); + codeOut.writeUnsignedShort(tries.length); + + int debugInfoOffset = code.getDebugInfoOffset(); + if (debugInfoOffset != 0) { + codeOut.writeInt(debugInfoOut.getPosition()); + transformDebugInfoItem(in.open(debugInfoOffset), indexMap); + } else { + codeOut.writeInt(0); } - /** - * Writes the catch handlers to {@code codeOut} and returns their indices. - */ - private int[] transformCatchHandlers(IndexMap indexMap, Code.CatchHandler[] catchHandlers) { - int baseOffset = codeOut.getPosition(); - codeOut.writeUleb128(catchHandlers.length); - int[] offsets = new int[catchHandlers.length]; - for (int i = 0; i < catchHandlers.length; i++) { - offsets[i] = codeOut.getPosition() - baseOffset; - transformEncodedCatchHandler(catchHandlers[i], indexMap); - } - return offsets; + short[] instructions = code.getInstructions(); + InstructionTransformer transformer = + (in == dexA) ? aInstructionTransformer : bInstructionTransformer; + short[] newInstructions = transformer.transform(instructions); + codeOut.writeInt(newInstructions.length); + codeOut.write(newInstructions); + + if (tries.length > 0) { + if (newInstructions.length % 2 == 1) { + codeOut.writeShort((short) 0); // padding + } + + /* + * We can't write the tries until we've written the catch handlers. + * Unfortunately they're in the opposite order in the dex file so we + * need to transform them out-of-order. + */ + DexBuffer.Section triesSection = dexOut.open(codeOut.getPosition()); + codeOut.skip(tries.length * SizeOf.TRY_ITEM); + int[] offsets = transformCatchHandlers(indexMap, catchHandlers); + transformTries(triesSection, tries, offsets); } - - private void transformTries(DexBuffer.Section out, Code.Try[] tries, - int[] catchHandlerOffsets) { - for (Code.Try tryItem : tries) { - out.writeInt(tryItem.getStartAddress()); - out.writeUnsignedShort(tryItem.getInstructionCount()); - out.writeUnsignedShort(catchHandlerOffsets[tryItem.getCatchHandlerIndex()]); - } + } + + /** + * Writes the catch handlers to {@code codeOut} and returns their indices. + */ + private int[] transformCatchHandlers(IndexMap indexMap, Code.CatchHandler[] catchHandlers) { + int baseOffset = codeOut.getPosition(); + codeOut.writeUleb128(catchHandlers.length); + int[] offsets = new int[catchHandlers.length]; + for (int i = 0; i < catchHandlers.length; i++) { + offsets[i] = codeOut.getPosition() - baseOffset; + transformEncodedCatchHandler(catchHandlers[i], indexMap); } - - private static final byte DBG_END_SEQUENCE = 0x00; - private static final byte DBG_ADVANCE_PC = 0x01; - private static final byte DBG_ADVANCE_LINE = 0x02; - private static final byte DBG_START_LOCAL = 0x03; - private static final byte DBG_START_LOCAL_EXTENDED = 0x04; - private static final byte DBG_END_LOCAL = 0x05; - private static final byte DBG_RESTART_LOCAL = 0x06; - private static final byte DBG_SET_PROLOGUE_END = 0x07; - private static final byte DBG_SET_EPILOGUE_BEGIN = 0x08; - private static final byte DBG_SET_FILE = 0x09; - - private void transformDebugInfoItem(DexBuffer.Section in, IndexMap indexMap) { - contentsOut.debugInfos.size++; - int lineStart = in.readUleb128(); - debugInfoOut.writeUleb128(lineStart); - - int parametersSize = in.readUleb128(); - debugInfoOut.writeUleb128(parametersSize); - - for (int p = 0; p < parametersSize; p++) { - int parameterName = in.readUleb128p1(); - debugInfoOut.writeUleb128p1(indexMap.adjustString(parameterName)); - } - - int addrDiff; // uleb128 address delta. - int lineDiff; // sleb128 line delta. - int registerNum; // uleb128 register number. - int nameIndex; // uleb128p1 string index. Needs indexMap adjustment. - int typeIndex; // uleb128p1 type index. Needs indexMap adjustment. - int sigIndex; // uleb128p1 string index. Needs indexMap adjustment. - - while (true) { - int opcode = in.readByte(); - debugInfoOut.writeByte(opcode); - - switch (opcode) { - case DBG_END_SEQUENCE: - return; - - case DBG_ADVANCE_PC: - addrDiff = in.readUleb128(); - debugInfoOut.writeUleb128(addrDiff); - break; - - case DBG_ADVANCE_LINE: - lineDiff = in.readSleb128(); - debugInfoOut.writeSleb128(lineDiff); - break; - - case DBG_START_LOCAL: - case DBG_START_LOCAL_EXTENDED: - registerNum = in.readUleb128(); - debugInfoOut.writeUleb128(registerNum); - nameIndex = in.readUleb128p1(); - debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex)); - typeIndex = in.readUleb128p1(); - debugInfoOut.writeUleb128p1(indexMap.adjustType(typeIndex)); - if (opcode == DBG_START_LOCAL_EXTENDED) { - sigIndex = in.readUleb128p1(); - debugInfoOut.writeUleb128p1(indexMap.adjustString(sigIndex)); - } - break; - - case DBG_END_LOCAL: - case DBG_RESTART_LOCAL: - registerNum = in.readUleb128(); - debugInfoOut.writeUleb128(registerNum); - break; - - case DBG_SET_FILE: - nameIndex = in.readUleb128p1(); - debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex)); - break; - - case DBG_SET_PROLOGUE_END: - case DBG_SET_EPILOGUE_BEGIN: - default: - break; - } - } + return offsets; + } + + private void transformTries(DexBuffer.Section out, Code.Try[] tries, int[] catchHandlerOffsets) { + for (Code.Try tryItem : tries) { + out.writeInt(tryItem.getStartAddress()); + out.writeUnsignedShort(tryItem.getInstructionCount()); + out.writeUnsignedShort(catchHandlerOffsets[tryItem.getCatchHandlerIndex()]); + } + } + + private static final byte DBG_END_SEQUENCE = 0x00; + private static final byte DBG_ADVANCE_PC = 0x01; + private static final byte DBG_ADVANCE_LINE = 0x02; + private static final byte DBG_START_LOCAL = 0x03; + private static final byte DBG_START_LOCAL_EXTENDED = 0x04; + private static final byte DBG_END_LOCAL = 0x05; + private static final byte DBG_RESTART_LOCAL = 0x06; + private static final byte DBG_SET_PROLOGUE_END = 0x07; + private static final byte DBG_SET_EPILOGUE_BEGIN = 0x08; + private static final byte DBG_SET_FILE = 0x09; + + private void transformDebugInfoItem(DexBuffer.Section in, IndexMap indexMap) { + contentsOut.debugInfos.size++; + int lineStart = in.readUleb128(); + debugInfoOut.writeUleb128(lineStart); + + int parametersSize = in.readUleb128(); + debugInfoOut.writeUleb128(parametersSize); + + for (int p = 0; p < parametersSize; p++) { + int parameterName = in.readUleb128p1(); + debugInfoOut.writeUleb128p1(indexMap.adjustString(parameterName)); } - private void transformEncodedCatchHandler(Code.CatchHandler catchHandler, IndexMap indexMap) { - int catchAllAddress = catchHandler.getCatchAllAddress(); - int[] typeIndexes = catchHandler.getTypeIndexes(); - int[] addresses = catchHandler.getAddresses(); + int addrDiff; // uleb128 address delta. + int lineDiff; // sleb128 line delta. + int registerNum; // uleb128 register number. + int nameIndex; // uleb128p1 string index. Needs indexMap adjustment. + int typeIndex; // uleb128p1 type index. Needs indexMap adjustment. + int sigIndex; // uleb128p1 string index. Needs indexMap adjustment. + + while (true) { + int opcode = in.readByte(); + debugInfoOut.writeByte(opcode); + + switch (opcode) { + case DBG_END_SEQUENCE: + return; + + case DBG_ADVANCE_PC: + addrDiff = in.readUleb128(); + debugInfoOut.writeUleb128(addrDiff); + break; + + case DBG_ADVANCE_LINE: + lineDiff = in.readSleb128(); + debugInfoOut.writeSleb128(lineDiff); + break; + + case DBG_START_LOCAL: + case DBG_START_LOCAL_EXTENDED: + registerNum = in.readUleb128(); + debugInfoOut.writeUleb128(registerNum); + nameIndex = in.readUleb128p1(); + debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex)); + typeIndex = in.readUleb128p1(); + debugInfoOut.writeUleb128p1(indexMap.adjustType(typeIndex)); + if (opcode == DBG_START_LOCAL_EXTENDED) { + sigIndex = in.readUleb128p1(); + debugInfoOut.writeUleb128p1(indexMap.adjustString(sigIndex)); + } + break; + + case DBG_END_LOCAL: + case DBG_RESTART_LOCAL: + registerNum = in.readUleb128(); + debugInfoOut.writeUleb128(registerNum); + break; + + case DBG_SET_FILE: + nameIndex = in.readUleb128p1(); + debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex)); + break; + + case DBG_SET_PROLOGUE_END: + case DBG_SET_EPILOGUE_BEGIN: + default: + break; + } + } + } - if (catchAllAddress != -1) { - codeOut.writeSleb128(-typeIndexes.length); - } else { - codeOut.writeSleb128(typeIndexes.length); - } + private void transformEncodedCatchHandler(Code.CatchHandler catchHandler, IndexMap indexMap) { + int catchAllAddress = catchHandler.getCatchAllAddress(); + int[] typeIndexes = catchHandler.getTypeIndexes(); + int[] addresses = catchHandler.getAddresses(); - for (int i = 0; i < typeIndexes.length; i++) { - codeOut.writeUleb128(indexMap.adjustType(typeIndexes[i])); - codeOut.writeUleb128(addresses[i]); - } + if (catchAllAddress != -1) { + codeOut.writeSleb128(-typeIndexes.length); + } else { + codeOut.writeSleb128(typeIndexes.length); + } - if (catchAllAddress != -1) { - codeOut.writeUleb128(catchAllAddress); - } + for (int i = 0; i < typeIndexes.length; i++) { + codeOut.writeUleb128(indexMap.adjustType(typeIndexes[i])); + codeOut.writeUleb128(addresses[i]); } - private void transformStaticValues(DexBuffer.Section in, IndexMap indexMap) { - contentsOut.encodedArrays.size++; - indexMap.putStaticValuesOffset(in.getPosition(), encodedArrayOut.getPosition()); - indexMap.adjustEncodedArray(in.readEncodedArray()).writeTo(encodedArrayOut); + if (catchAllAddress != -1) { + codeOut.writeUleb128(catchAllAddress); } + } + + private void transformStaticValues(DexBuffer.Section in, IndexMap indexMap) { + contentsOut.encodedArrays.size++; + indexMap.putStaticValuesOffset(in.getPosition(), encodedArrayOut.getPosition()); + indexMap.adjustEncodedArray(in.readEncodedArray()).writeTo(encodedArrayOut); + } + + /** + * Byte counts for the sections written when creating a dex. Target sizes + * are defined in one of two ways: + * <ul> + * <li>By pessimistically guessing how large the union of dex files will be. + * We're pessimistic because we can't predict the amount of duplication + * between dex files, nor can we predict the length of ULEB-encoded + * offsets or indices. + * <li>By exactly measuring an existing dex. + * </ul> + */ + private static class WriterSizes { + private int header = SizeOf.HEADER_ITEM; + private int idsDefs; + private int mapList; + private int typeList; + private int classData; + private int code; + private int stringData; + private int debugInfo; + private int encodedArray; + private int annotationsDirectory; + private int annotationsSet; + private int annotationsSetRefList; + private int annotation; /** - * Byte counts for the sections written when creating a dex. Target sizes - * are defined in one of two ways: - * <ul> - * <li>By pessimistically guessing how large the union of dex files will be. - * We're pessimistic because we can't predict the amount of duplication - * between dex files, nor can we predict the length of ULEB-encoded - * offsets or indices. - * <li>By exactly measuring an existing dex. - * </ul> + * Compute sizes for merging a and b. */ - private static class WriterSizes { - private int header = SizeOf.HEADER_ITEM; - private int idsDefs; - private int mapList; - private int typeList; - private int classData; - private int code; - private int stringData; - private int debugInfo; - private int encodedArray; - private int annotationsDirectory; - private int annotationsSet; - private int annotationsSetRefList; - private int annotation; - - /** - * Compute sizes for merging a and b. - */ - public WriterSizes(DexBuffer a, DexBuffer b) { - plus(a.getTableOfContents(), false); - plus(b.getTableOfContents(), false); - } - - public WriterSizes(DexMerger dexMerger) { - header = dexMerger.headerOut.used(); - idsDefs = dexMerger.idsDefsOut.used(); - mapList = dexMerger.mapListOut.used(); - typeList = dexMerger.typeListOut.used(); - classData = dexMerger.classDataOut.used(); - code = dexMerger.codeOut.used(); - stringData = dexMerger.stringDataOut.used(); - debugInfo = dexMerger.debugInfoOut.used(); - encodedArray = dexMerger.encodedArrayOut.used(); - annotationsDirectory = dexMerger.annotationsDirectoryOut.used(); - annotationsSet = dexMerger.annotationSetOut.used(); - annotationsSetRefList = dexMerger.annotationSetRefListOut.used(); - annotation = dexMerger.annotationOut.used(); - } + public WriterSizes(DexBuffer a, DexBuffer b) { + plus(a.getTableOfContents(), false); + plus(b.getTableOfContents(), false); + } - public void plus(TableOfContents contents, boolean exact) { - idsDefs += contents.stringIds.size * SizeOf.STRING_ID_ITEM - + contents.typeIds.size * SizeOf.TYPE_ID_ITEM - + contents.protoIds.size * SizeOf.PROTO_ID_ITEM - + contents.fieldIds.size * SizeOf.MEMBER_ID_ITEM - + contents.methodIds.size * SizeOf.MEMBER_ID_ITEM - + contents.classDefs.size * SizeOf.CLASS_DEF_ITEM; - mapList = SizeOf.UINT + (contents.sections.length * SizeOf.MAP_ITEM); - typeList += contents.typeLists.byteCount; - stringData += contents.stringDatas.byteCount; - annotationsDirectory += contents.annotationsDirectories.byteCount; - annotationsSet += contents.annotationSets.byteCount; - annotationsSetRefList += contents.annotationSetRefLists.byteCount; - - if (exact) { - code += contents.codes.byteCount; - classData += contents.classDatas.byteCount; - encodedArray += contents.encodedArrays.byteCount; - annotation += contents.annotations.byteCount; - debugInfo += contents.debugInfos.byteCount; - } else { - // at most 1/4 of the bytes in a code section are uleb/sleb - code += (int) Math.ceil(contents.codes.byteCount * 1.25); - // at most 1/3 of the bytes in a class data section are uleb/sleb - classData += (int) Math.ceil(contents.classDatas.byteCount * 1.34); - // all of the bytes in an encoding arrays section may be uleb/sleb - encodedArray += contents.encodedArrays.byteCount * 2; - // all of the bytes in an annotations section may be uleb/sleb - annotation += (int) Math.ceil(contents.annotations.byteCount * 2); - // all of the bytes in a debug info section may be uleb/sleb - debugInfo += contents.debugInfos.byteCount * 2; - } - - typeList = DexBuffer.fourByteAlign(typeList); - code = DexBuffer.fourByteAlign(code); - } + public WriterSizes(DexMerger dexMerger) { + header = dexMerger.headerOut.used(); + idsDefs = dexMerger.idsDefsOut.used(); + mapList = dexMerger.mapListOut.used(); + typeList = dexMerger.typeListOut.used(); + classData = dexMerger.classDataOut.used(); + code = dexMerger.codeOut.used(); + stringData = dexMerger.stringDataOut.used(); + debugInfo = dexMerger.debugInfoOut.used(); + encodedArray = dexMerger.encodedArrayOut.used(); + annotationsDirectory = dexMerger.annotationsDirectoryOut.used(); + annotationsSet = dexMerger.annotationSetOut.used(); + annotationsSetRefList = dexMerger.annotationSetRefListOut.used(); + annotation = dexMerger.annotationOut.used(); + } - public int size() { - return header + idsDefs + mapList + typeList + classData + code + stringData + debugInfo - + encodedArray + annotationsDirectory + annotationsSet + annotationsSetRefList - + annotation; - } + public void plus(TableOfContents contents, boolean exact) { + idsDefs += contents.stringIds.size * SizeOf.STRING_ID_ITEM + contents.typeIds.size + * SizeOf.TYPE_ID_ITEM + contents.protoIds.size * SizeOf.PROTO_ID_ITEM + + contents.fieldIds.size * SizeOf.MEMBER_ID_ITEM + contents.methodIds.size + * SizeOf.MEMBER_ID_ITEM + contents.classDefs.size * SizeOf.CLASS_DEF_ITEM; + mapList = SizeOf.UINT + (contents.sections.length * SizeOf.MAP_ITEM); + typeList += contents.typeLists.byteCount; + stringData += contents.stringDatas.byteCount; + annotationsDirectory += contents.annotationsDirectories.byteCount; + annotationsSet += contents.annotationSets.byteCount; + annotationsSetRefList += contents.annotationSetRefLists.byteCount; + + if (exact) { + code += contents.codes.byteCount; + classData += contents.classDatas.byteCount; + encodedArray += contents.encodedArrays.byteCount; + annotation += contents.annotations.byteCount; + debugInfo += contents.debugInfos.byteCount; + } else { + // at most 1/4 of the bytes in a code section are uleb/sleb + code += (int) Math.ceil(contents.codes.byteCount * 1.25); + // at most 1/3 of the bytes in a class data section are uleb/sleb + classData += (int) Math.ceil(contents.classDatas.byteCount * 1.34); + // all of the bytes in an encoding arrays section may be uleb/sleb + encodedArray += contents.encodedArrays.byteCount * 2; + // all of the bytes in an annotations section may be uleb/sleb + annotation += (int) Math.ceil(contents.annotations.byteCount * 2); + // all of the bytes in a debug info section may be uleb/sleb + debugInfo += contents.debugInfos.byteCount * 2; + } + + typeList = DexBuffer.fourByteAlign(typeList); + code = DexBuffer.fourByteAlign(code); } - public static void main(String[] args) throws IOException { - if (args.length < 2) { - printUsage(); - return; - } + public int size() { + return header + idsDefs + mapList + typeList + classData + code + stringData + debugInfo + + encodedArray + annotationsDirectory + annotationsSet + annotationsSetRefList + + annotation; + } + } - DexBuffer merged = new DexBuffer(new File(args[1])); - for (int i = 2; i < args.length; i++) { - DexBuffer toMerge = new DexBuffer(new File(args[i])); - merged = new DexMerger(merged, toMerge, CollisionPolicy.KEEP_FIRST).merge(); - } - merged.writeTo(new File(args[0])); + public static void main(String[] args) throws IOException { + if (args.length < 2) { + printUsage(); + return; } - private static void printUsage() { - System.out.println("Usage: DexMerger <out.dex> <a.dex> <b.dex> ..."); - System.out.println(); - System.out.println( - "If a class is defined in several dex, the class found in the first dex will be used."); + DexBuffer merged = new DexBuffer(new File(args[1])); + for (int i = 2; i < args.length; i++) { + DexBuffer toMerge = new DexBuffer(new File(args[i])); + merged = new DexMerger(merged, toMerge, CollisionPolicy.KEEP_FIRST).merge(); } + merged.writeTo(new File(args[0])); + } + + private static void printUsage() { + System.out.println("Usage: DexMerger <out.dex> <a.dex> <b.dex> ..."); + System.out.println(); + System.out.println( + "If a class is defined in several dex, the class found in the first dex will be used."); + } } diff --git a/dx/src/com/android/jack/dx/merge/IndexMap.java b/dx/src/com/android/jack/dx/merge/IndexMap.java index 37cd30e..59904f3 100644 --- a/dx/src/com/android/jack/dx/merge/IndexMap.java +++ b/dx/src/com/android/jack/dx/merge/IndexMap.java @@ -39,269 +39,281 @@ import java.util.HashMap; * {@code strings[5]}. */ public final class IndexMap { - private final DexBuffer target; - public final int[] stringIds; - public final short[] typeIds; - public final short[] protoIds; - public final short[] fieldIds; - public final short[] methodIds; - private final HashMap<Integer, Integer> typeListOffsets; - private final HashMap<Integer, Integer> annotationOffsets; - private final HashMap<Integer, Integer> annotationSetOffsets; - private final HashMap<Integer, Integer> annotationDirectoryOffsets; - private final HashMap<Integer, Integer> staticValuesOffsets; - - public IndexMap(DexBuffer target, TableOfContents tableOfContents) { - this.target = target; - this.stringIds = new int[tableOfContents.stringIds.size]; - this.typeIds = new short[tableOfContents.typeIds.size]; - this.protoIds = new short[tableOfContents.protoIds.size]; - this.fieldIds = new short[tableOfContents.fieldIds.size]; - this.methodIds = new short[tableOfContents.methodIds.size]; - this.typeListOffsets = new HashMap<Integer, Integer>(); - this.annotationOffsets = new HashMap<Integer, Integer>(); - this.annotationSetOffsets = new HashMap<Integer, Integer>(); - this.annotationDirectoryOffsets = new HashMap<Integer, Integer>(); - this.staticValuesOffsets = new HashMap<Integer, Integer>(); - - /* - * A type list, annotation set, annotation directory, or static value at - * offset 0 is always empty. Always map offset 0 to 0. - */ - this.typeListOffsets.put(0, 0); - this.annotationSetOffsets.put(0, 0); - this.annotationDirectoryOffsets.put(0, 0); - this.staticValuesOffsets.put(0, 0); + private final DexBuffer target; + public final int[] stringIds; + public final short[] typeIds; + public final short[] protoIds; + public final short[] fieldIds; + public final short[] methodIds; + private final HashMap<Integer, Integer> typeListOffsets; + private final HashMap<Integer, Integer> annotationOffsets; + private final HashMap<Integer, Integer> annotationSetOffsets; + private final HashMap<Integer, Integer> annotationDirectoryOffsets; + private final HashMap<Integer, Integer> staticValuesOffsets; + + public IndexMap(DexBuffer target, TableOfContents tableOfContents) { + this.target = target; + this.stringIds = new int[tableOfContents.stringIds.size]; + this.typeIds = new short[tableOfContents.typeIds.size]; + this.protoIds = new short[tableOfContents.protoIds.size]; + this.fieldIds = new short[tableOfContents.fieldIds.size]; + this.methodIds = new short[tableOfContents.methodIds.size]; + this.typeListOffsets = new HashMap<Integer, Integer>(); + this.annotationOffsets = new HashMap<Integer, Integer>(); + this.annotationSetOffsets = new HashMap<Integer, Integer>(); + this.annotationDirectoryOffsets = new HashMap<Integer, Integer>(); + this.staticValuesOffsets = new HashMap<Integer, Integer>(); + + /* + * A type list, annotation set, annotation directory, or static value at + * offset 0 is always empty. Always map offset 0 to 0. + */ + this.typeListOffsets.put(0, 0); + this.annotationSetOffsets.put(0, 0); + this.annotationDirectoryOffsets.put(0, 0); + this.staticValuesOffsets.put(0, 0); + } + + public void putTypeListOffset(int oldOffset, int newOffset) { + if (oldOffset <= 0 || newOffset <= 0) { + throw new IllegalArgumentException(); } + typeListOffsets.put(oldOffset, newOffset); + } - public void putTypeListOffset(int oldOffset, int newOffset) { - if (oldOffset <= 0 || newOffset <= 0) { - throw new IllegalArgumentException(); - } - typeListOffsets.put(oldOffset, newOffset); + public void putAnnotationOffset(int oldOffset, int newOffset) { + if (oldOffset <= 0 || newOffset <= 0) { + throw new IllegalArgumentException(); } + annotationOffsets.put(oldOffset, newOffset); + } - public void putAnnotationOffset(int oldOffset, int newOffset) { - if (oldOffset <= 0 || newOffset <= 0) { - throw new IllegalArgumentException(); - } - annotationOffsets.put(oldOffset, newOffset); + public void putAnnotationSetOffset(int oldOffset, int newOffset) { + if (oldOffset <= 0 || newOffset <= 0) { + throw new IllegalArgumentException(); } + annotationSetOffsets.put(oldOffset, newOffset); + } - public void putAnnotationSetOffset(int oldOffset, int newOffset) { - if (oldOffset <= 0 || newOffset <= 0) { - throw new IllegalArgumentException(); - } - annotationSetOffsets.put(oldOffset, newOffset); + public void putAnnotationDirectoryOffset(int oldOffset, int newOffset) { + if (oldOffset <= 0 || newOffset <= 0) { + throw new IllegalArgumentException(); } + annotationDirectoryOffsets.put(oldOffset, newOffset); + } - public void putAnnotationDirectoryOffset(int oldOffset, int newOffset) { - if (oldOffset <= 0 || newOffset <= 0) { - throw new IllegalArgumentException(); - } - annotationDirectoryOffsets.put(oldOffset, newOffset); + public void putStaticValuesOffset(int oldOffset, int newOffset) { + if (oldOffset <= 0 || newOffset <= 0) { + throw new IllegalArgumentException(); } + staticValuesOffsets.put(oldOffset, newOffset); + } - public void putStaticValuesOffset(int oldOffset, int newOffset) { - if (oldOffset <= 0 || newOffset <= 0) { - throw new IllegalArgumentException(); - } - staticValuesOffsets.put(oldOffset, newOffset); - } + public int adjustString(int stringIndex) { + return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex]; + } - public int adjustString(int stringIndex) { - return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex]; - } - - public int adjustType(int typeIndex) { - return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : (typeIds[typeIndex] & 0xffff); - } + public int adjustType(int typeIndex) { + return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : (typeIds[typeIndex] & 0xffff); + } - public TypeList adjustTypeList(TypeList typeList) { - if (typeList == TypeList.EMPTY) { - return typeList; - } - short[] types = typeList.getTypes().clone(); - for (int i = 0; i < types.length; i++) { - types[i] = (short) adjustType(types[i]); - } - return new TypeList(target, types); + public TypeList adjustTypeList(TypeList typeList) { + if (typeList == TypeList.EMPTY) { + return typeList; } - - public int adjustProto(int protoIndex) { - return protoIds[protoIndex] & 0xffff; + short[] types = typeList.getTypes().clone(); + for (int i = 0; i < types.length; i++) { + types[i] = (short) adjustType(types[i]); } - - public int adjustField(int fieldIndex) { - return fieldIds[fieldIndex] & 0xffff; + return new TypeList(target, types); + } + + public int adjustProto(int protoIndex) { + return protoIds[protoIndex] & 0xffff; + } + + public int adjustField(int fieldIndex) { + return fieldIds[fieldIndex] & 0xffff; + } + + public int adjustMethod(int methodIndex) { + return methodIds[methodIndex] & 0xffff; + } + + public int adjustTypeListOffset(int typeListOffset) { + return typeListOffsets.get(typeListOffset); + } + + public int adjustAnnotation(int annotationOffset) { + return annotationOffsets.get(annotationOffset); + } + + public int adjustAnnotationSet(int annotationSetOffset) { + return annotationSetOffsets.get(annotationSetOffset); + } + + public int adjustAnnotationDirectory(int annotationDirectoryOffset) { + return annotationDirectoryOffsets.get(annotationDirectoryOffset); + } + + public int adjustStaticValues(int staticValuesOffset) { + return staticValuesOffsets.get(staticValuesOffset); + } + + public MethodId adjust(MethodId methodId) { + return new MethodId(target, adjustType(methodId.getDeclaringClassIndex()), + adjustProto(methodId.getProtoIndex()), adjustString(methodId.getNameIndex())); + } + + public FieldId adjust(FieldId fieldId) { + return new FieldId(target, adjustType(fieldId.getDeclaringClassIndex()), + adjustType(fieldId.getTypeIndex()), adjustString(fieldId.getNameIndex())); + + } + + public ProtoId adjust(ProtoId protoId) { + return new ProtoId(target, adjustString(protoId.getShortyIndex()), + adjustType(protoId.getReturnTypeIndex()), + adjustTypeListOffset(protoId.getParametersOffset())); + } + + public ClassDef adjust(ClassDef classDef) { + return new ClassDef(target, + classDef.getOffset(), + adjustType(classDef.getTypeIndex()), + classDef.getAccessFlags(), + adjustType(classDef.getSupertypeIndex()), + adjustTypeListOffset(classDef.getInterfacesOffset()), + classDef.getSourceFileIndex(), + classDef.getAnnotationsOffset(), + classDef.getClassDataOffset(), + classDef.getStaticValuesOffset()); + } + + public SortableType adjust(SortableType sortableType) { + return new SortableType(sortableType.getBuffer(), adjust(sortableType.getClassDef())); + } + + public EncodedValue adjustEncodedValue(EncodedValue encodedValue) { + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); + new EncodedValueTransformer(encodedValue, out).readValue(); + return new EncodedValue(out.toByteArray()); + } + + public EncodedValue adjustEncodedArray(EncodedValue encodedArray) { + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); + new EncodedValueTransformer(encodedArray, out).readArray(); + return new EncodedValue(out.toByteArray()); + } + + public Annotation adjust(Annotation annotation) { + int[] names = annotation.getNames().clone(); + EncodedValue[] values = annotation.getValues().clone(); + for (int i = 0; i < names.length; i++) { + names[i] = adjustString(names[i]); + values[i] = adjustEncodedValue(values[i]); } - - public int adjustMethod(int methodIndex) { - return methodIds[methodIndex] & 0xffff; + return new Annotation(target, annotation.getVisibility(), adjustType(annotation.getTypeIndex()), + names, values); + } + + /** + * Adjust an encoded value or array. + */ + private final class EncodedValueTransformer extends EncodedValueReader { + private final ByteOutput out; + + public EncodedValueTransformer(EncodedValue encodedValue, ByteOutput out) { + super(encodedValue); + this.out = out; } - public int adjustTypeListOffset(int typeListOffset) { - return typeListOffsets.get(typeListOffset); + @Override + protected void visitArray(int size) { + Leb128Utils.writeUnsignedLeb128(out, size); } - public int adjustAnnotation(int annotationOffset) { - return annotationOffsets.get(annotationOffset); + @Override + protected void visitAnnotation(int typeIndex, int size) { + Leb128Utils.writeUnsignedLeb128(out, adjustType(typeIndex)); + Leb128Utils.writeUnsignedLeb128(out, size); } - public int adjustAnnotationSet(int annotationSetOffset) { - return annotationSetOffsets.get(annotationSetOffset); + @Override + protected void visitAnnotationName(int index) { + Leb128Utils.writeUnsignedLeb128(out, adjustString(index)); } - public int adjustAnnotationDirectory(int annotationDirectoryOffset) { - return annotationDirectoryOffsets.get(annotationDirectoryOffset); + @Override + protected void visitPrimitive(int argAndType, int type, int arg, int size) { + out.writeByte(argAndType); + copyBytes(in, out, size); } - public int adjustStaticValues(int staticValuesOffset) { - return staticValuesOffsets.get(staticValuesOffset); + @Override + protected void visitString(int type, int index) { + writeTypeAndSizeAndIndex(type, adjustString(index)); } - public MethodId adjust(MethodId methodId) { - return new MethodId(target, - adjustType(methodId.getDeclaringClassIndex()), - adjustProto(methodId.getProtoIndex()), - adjustString(methodId.getNameIndex())); + @Override + protected void visitType(int type, int index) { + writeTypeAndSizeAndIndex(type, adjustType(index)); } - public FieldId adjust(FieldId fieldId) { - return new FieldId(target, - adjustType(fieldId.getDeclaringClassIndex()), - adjustType(fieldId.getTypeIndex()), - adjustString(fieldId.getNameIndex())); - + @Override + protected void visitField(int type, int index) { + writeTypeAndSizeAndIndex(type, adjustField(index)); } - public ProtoId adjust(ProtoId protoId) { - return new ProtoId(target, - adjustString(protoId.getShortyIndex()), - adjustType(protoId.getReturnTypeIndex()), - adjustTypeListOffset(protoId.getParametersOffset())); + @Override + protected void visitMethod(int type, int index) { + writeTypeAndSizeAndIndex(type, adjustMethod(index)); } - public ClassDef adjust(ClassDef classDef) { - return new ClassDef(target, classDef.getOffset(), adjustType(classDef.getTypeIndex()), - classDef.getAccessFlags(), adjustType(classDef.getSupertypeIndex()), - adjustTypeListOffset(classDef.getInterfacesOffset()), classDef.getSourceFileIndex(), - classDef.getAnnotationsOffset(), classDef.getClassDataOffset(), - classDef.getStaticValuesOffset()); + @Override + protected void visitArrayValue(int argAndType) { + out.writeByte(argAndType); } - public SortableType adjust(SortableType sortableType) { - return new SortableType(sortableType.getBuffer(), adjust(sortableType.getClassDef())); + @Override + protected void visitAnnotationValue(int argAndType) { + out.writeByte(argAndType); } - public EncodedValue adjustEncodedValue(EncodedValue encodedValue) { - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); - new EncodedValueTransformer(encodedValue, out).readValue(); - return new EncodedValue(out.toByteArray()); + @Override + protected void visitEncodedBoolean(int argAndType) { + out.writeByte(argAndType); } - public EncodedValue adjustEncodedArray(EncodedValue encodedArray) { - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32); - new EncodedValueTransformer(encodedArray, out).readArray(); - return new EncodedValue(out.toByteArray()); + @Override + protected void visitEncodedNull(int argAndType) { + out.writeByte(argAndType); } - public Annotation adjust(Annotation annotation) { - int[] names = annotation.getNames().clone(); - EncodedValue[] values = annotation.getValues().clone(); - for (int i = 0; i < names.length; i++) { - names[i] = adjustString(names[i]); - values[i] = adjustEncodedValue(values[i]); - } - return new Annotation(target, annotation.getVisibility(), - adjustType(annotation.getTypeIndex()), names, values); + private void writeTypeAndSizeAndIndex(int type, int index) { + int byteCount; + if (Unsigned.compare(index, 0xff) <= 0) { + byteCount = 1; + } else if (Unsigned.compare(index, 0xffff) <= 0) { + byteCount = 2; + } else if (Unsigned.compare(index, 0xffffff) <= 0) { + byteCount = 3; + } else { + byteCount = 4; + } + int argAndType = ((byteCount - 1) << 5) | type; + out.writeByte(argAndType); + + for (int i = 0; i < byteCount; i++) { + out.writeByte(index & 0xff); + index >>>= 8; + } } - /** - * Adjust an encoded value or array. - */ - private final class EncodedValueTransformer extends EncodedValueReader { - private final ByteOutput out; - - public EncodedValueTransformer(EncodedValue encodedValue, ByteOutput out) { - super(encodedValue); - this.out = out; - } - - protected void visitArray(int size) { - Leb128Utils.writeUnsignedLeb128(out, size); - } - - protected void visitAnnotation(int typeIndex, int size) { - Leb128Utils.writeUnsignedLeb128(out, adjustType(typeIndex)); - Leb128Utils.writeUnsignedLeb128(out, size); - } - - protected void visitAnnotationName(int index) { - Leb128Utils.writeUnsignedLeb128(out, adjustString(index)); - } - - protected void visitPrimitive(int argAndType, int type, int arg, int size) { - out.writeByte(argAndType); - copyBytes(in, out, size); - } - - protected void visitString(int type, int index) { - writeTypeAndSizeAndIndex(type, adjustString(index)); - } - - protected void visitType(int type, int index) { - writeTypeAndSizeAndIndex(type, adjustType(index)); - } - - protected void visitField(int type, int index) { - writeTypeAndSizeAndIndex(type, adjustField(index)); - } - - protected void visitMethod(int type, int index) { - writeTypeAndSizeAndIndex(type, adjustMethod(index)); - } - - protected void visitArrayValue(int argAndType) { - out.writeByte(argAndType); - } - - protected void visitAnnotationValue(int argAndType) { - out.writeByte(argAndType); - } - - protected void visitEncodedBoolean(int argAndType) { - out.writeByte(argAndType); - } - - protected void visitEncodedNull(int argAndType) { - out.writeByte(argAndType); - } - - private void writeTypeAndSizeAndIndex(int type, int index) { - int byteCount; - if (Unsigned.compare(index, 0xff) <= 0) { - byteCount = 1; - } else if (Unsigned.compare(index, 0xffff) <= 0) { - byteCount = 2; - } else if (Unsigned.compare(index, 0xffffff) <= 0) { - byteCount = 3; - } else { - byteCount = 4; - } - int argAndType = ((byteCount - 1) << 5) | type; - out.writeByte(argAndType); - - for (int i = 0; i < byteCount; i++) { - out.writeByte(index & 0xff); - index >>>= 8; - } - } - - private void copyBytes(ByteInput in, ByteOutput out, int size) { - for (int i = 0; i < size; i++) { - out.writeByte(in.readByte()); - } - } + private void copyBytes(ByteInput in, ByteOutput out, int size) { + for (int i = 0; i < size; i++) { + out.writeByte(in.readByte()); + } } + } } diff --git a/dx/src/com/android/jack/dx/merge/InstructionTransformer.java b/dx/src/com/android/jack/dx/merge/InstructionTransformer.java index 0aa5364..65d3a3b 100644 --- a/dx/src/com/android/jack/dx/merge/InstructionTransformer.java +++ b/dx/src/com/android/jack/dx/merge/InstructionTransformer.java @@ -23,90 +23,94 @@ import com.android.jack.dx.io.instructions.ShortArrayCodeOutput; import com.android.jack.dx.util.DexException; final class InstructionTransformer { - private final IndexMap indexMap; - private final CodeReader reader; - private DecodedInstruction[] mappedInstructions; - private int mappedAt; + private final IndexMap indexMap; + private final CodeReader reader; + private DecodedInstruction[] mappedInstructions; + private int mappedAt; - public InstructionTransformer(IndexMap indexMap) { - this.indexMap = indexMap; - this.reader = new CodeReader(); - this.reader.setAllVisitors(new GenericVisitor()); - this.reader.setStringVisitor(new StringVisitor()); - this.reader.setTypeVisitor(new TypeVisitor()); - this.reader.setFieldVisitor(new FieldVisitor()); - this.reader.setMethodVisitor(new MethodVisitor()); - } - - public short[] transform(short[] encodedInstructions) throws DexException { - DecodedInstruction[] decodedInstructions = - DecodedInstruction.decodeAll(encodedInstructions); - int size = decodedInstructions.length; + public InstructionTransformer(IndexMap indexMap) { + this.indexMap = indexMap; + this.reader = new CodeReader(); + this.reader.setAllVisitors(new GenericVisitor()); + this.reader.setStringVisitor(new StringVisitor()); + this.reader.setTypeVisitor(new TypeVisitor()); + this.reader.setFieldVisitor(new FieldVisitor()); + this.reader.setMethodVisitor(new MethodVisitor()); + } - mappedInstructions = new DecodedInstruction[size]; - mappedAt = 0; - reader.visitAll(decodedInstructions); + public short[] transform(short[] encodedInstructions) throws DexException { + DecodedInstruction[] decodedInstructions = DecodedInstruction.decodeAll(encodedInstructions); + int size = decodedInstructions.length; - ShortArrayCodeOutput out = new ShortArrayCodeOutput(size); - for (DecodedInstruction instruction : mappedInstructions) { - if (instruction != null) { - instruction.encode(out); - } - } + mappedInstructions = new DecodedInstruction[size]; + mappedAt = 0; + reader.visitAll(decodedInstructions); - return out.getArray(); + ShortArrayCodeOutput out = new ShortArrayCodeOutput(size); + for (DecodedInstruction instruction : mappedInstructions) { + if (instruction != null) { + instruction.encode(out); + } } - private class GenericVisitor implements CodeReader.Visitor { - public void visit(DecodedInstruction[] all, DecodedInstruction one) { - mappedInstructions[mappedAt++] = one; - } + return out.getArray(); + } + + private class GenericVisitor implements CodeReader.Visitor { + @Override + public void visit(DecodedInstruction[] all, DecodedInstruction one) { + mappedInstructions[mappedAt++] = one; } + } - private class StringVisitor implements CodeReader.Visitor { - public void visit(DecodedInstruction[] all, DecodedInstruction one) { - int stringId = one.getIndex(); - int mappedId = indexMap.adjustString(stringId); - boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); - jumboCheck(isJumbo, mappedId); - mappedInstructions[mappedAt++] = one.withIndex(mappedId); - } + private class StringVisitor implements CodeReader.Visitor { + @Override + public void visit(DecodedInstruction[] all, DecodedInstruction one) { + int stringId = one.getIndex(); + int mappedId = indexMap.adjustString(stringId); + boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); + jumboCheck(isJumbo, mappedId); + mappedInstructions[mappedAt++] = one.withIndex(mappedId); } + } - private class FieldVisitor implements CodeReader.Visitor { - public void visit(DecodedInstruction[] all, DecodedInstruction one) { - int fieldId = one.getIndex(); - int mappedId = indexMap.adjustField(fieldId); - boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); - jumboCheck(isJumbo, mappedId); - mappedInstructions[mappedAt++] = one.withIndex(mappedId); - } + private class FieldVisitor implements CodeReader.Visitor { + @Override + public void visit(DecodedInstruction[] all, DecodedInstruction one) { + int fieldId = one.getIndex(); + int mappedId = indexMap.adjustField(fieldId); + boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); + jumboCheck(isJumbo, mappedId); + mappedInstructions[mappedAt++] = one.withIndex(mappedId); } + } - private class TypeVisitor implements CodeReader.Visitor { - public void visit(DecodedInstruction[] all, DecodedInstruction one) { - int typeId = one.getIndex(); - int mappedId = indexMap.adjustType(typeId); - boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); - jumboCheck(isJumbo, mappedId); - mappedInstructions[mappedAt++] = one.withIndex(mappedId); - } + private class TypeVisitor implements CodeReader.Visitor { + @Override + public void visit(DecodedInstruction[] all, DecodedInstruction one) { + int typeId = one.getIndex(); + int mappedId = indexMap.adjustType(typeId); + boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); + jumboCheck(isJumbo, mappedId); + mappedInstructions[mappedAt++] = one.withIndex(mappedId); } + } - private class MethodVisitor implements CodeReader.Visitor { - public void visit(DecodedInstruction[] all, DecodedInstruction one) { - int methodId = one.getIndex(); - int mappedId = indexMap.adjustMethod(methodId); - boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); - jumboCheck(isJumbo, mappedId); - mappedInstructions[mappedAt++] = one.withIndex(mappedId); - } + private class MethodVisitor implements CodeReader.Visitor { + @Override + public void visit(DecodedInstruction[] all, DecodedInstruction one) { + int methodId = one.getIndex(); + int mappedId = indexMap.adjustMethod(methodId); + boolean isJumbo = (one.getOpcode() == Opcodes.CONST_STRING_JUMBO); + jumboCheck(isJumbo, mappedId); + mappedInstructions[mappedAt++] = one.withIndex(mappedId); } + } - private static void jumboCheck(boolean isJumbo, int newIndex) { - if (!isJumbo && (newIndex > 0xffff)) { - throw new DexException("Cannot merge new index " + newIndex + - " into a non-jumbo instruction!"); - } + private static void jumboCheck(boolean isJumbo, int newIndex) { + if (!isJumbo && (newIndex > 0xffff)) { + throw new DexException( + "Cannot merge new index " + newIndex + " into a non-jumbo instruction!"); } + } } diff --git a/dx/src/com/android/jack/dx/merge/SortableType.java b/dx/src/com/android/jack/dx/merge/SortableType.java index 9435619..a479a89 100644 --- a/dx/src/com/android/jack/dx/merge/SortableType.java +++ b/dx/src/com/android/jack/dx/merge/SortableType.java @@ -26,81 +26,82 @@ import java.util.Comparator; * preceded by its supertype and implemented interfaces. */ final class SortableType { - public static final Comparator<SortableType> NULLS_LAST_ORDER = new Comparator<SortableType>() { - public int compare(SortableType a, SortableType b) { - if (a == b) { - return 0; - } - if (b == null) { - return -1; - } - if (a == null) { - return 1; - } - if (a.depth != b.depth) { - return a.depth - b.depth; - } - return a.getTypeIndex() - b.getTypeIndex(); - } - }; - - private final DexBuffer buffer; - private ClassDef classDef; - private int depth = -1; - - public SortableType(DexBuffer buffer, ClassDef classDef) { - this.buffer = buffer; - this.classDef = classDef; + public static final Comparator<SortableType> NULLS_LAST_ORDER = new Comparator<SortableType>() { + @Override + public int compare(SortableType a, SortableType b) { + if (a == b) { + return 0; + } + if (b == null) { + return -1; + } + if (a == null) { + return 1; + } + if (a.depth != b.depth) { + return a.depth - b.depth; + } + return a.getTypeIndex() - b.getTypeIndex(); } + }; - public DexBuffer getBuffer() { - return buffer; - } + private final DexBuffer buffer; + private ClassDef classDef; + private int depth = -1; - public ClassDef getClassDef() { - return classDef; - } + public SortableType(DexBuffer buffer, ClassDef classDef) { + this.buffer = buffer; + this.classDef = classDef; + } - public int getTypeIndex() { - return classDef.getTypeIndex(); - } + public DexBuffer getBuffer() { + return buffer; + } - /** - * Assigns this type's depth if the depths of its supertype and implemented - * interfaces are known. Returns false if the depth couldn't be computed - * yet. - */ - public boolean tryAssignDepth(SortableType[] types) { - int max; - if (classDef.getSupertypeIndex() == ClassDef.NO_INDEX) { - max = 0; // this is Object.class or an interface - } else { - SortableType sortableSupertype = types[classDef.getSupertypeIndex()]; - if (sortableSupertype == null) { - max = 1; // unknown, so assume it's a root. - } else if (sortableSupertype.depth == -1) { - return false; - } else { - max = sortableSupertype.depth; - } - } + public ClassDef getClassDef() { + return classDef; + } - for (short interfaceIndex : classDef.getInterfaces()) { - SortableType implemented = types[interfaceIndex]; - if (implemented == null) { - max = Math.max(max, 1); // unknown, so assume it's a root. - } else if (implemented.depth == -1) { - return false; - } else { - max = Math.max(max, implemented.depth); - } - } + public int getTypeIndex() { + return classDef.getTypeIndex(); + } - depth = max + 1; - return true; + /** + * Assigns this type's depth if the depths of its supertype and implemented + * interfaces are known. Returns false if the depth couldn't be computed + * yet. + */ + public boolean tryAssignDepth(SortableType[] types) { + int max; + if (classDef.getSupertypeIndex() == ClassDef.NO_INDEX) { + max = 0; // this is Object.class or an interface + } else { + SortableType sortableSupertype = types[classDef.getSupertypeIndex()]; + if (sortableSupertype == null) { + max = 1; // unknown, so assume it's a root. + } else if (sortableSupertype.depth == -1) { + return false; + } else { + max = sortableSupertype.depth; + } } - public boolean isDepthAssigned() { - return depth != -1; + for (short interfaceIndex : classDef.getInterfaces()) { + SortableType implemented = types[interfaceIndex]; + if (implemented == null) { + max = Math.max(max, 1); // unknown, so assume it's a root. + } else if (implemented.depth == -1) { + return false; + } else { + max = Math.max(max, implemented.depth); + } } + + depth = max + 1; + return true; + } + + public boolean isDepthAssigned() { + return depth != -1; + } } diff --git a/dx/src/com/android/jack/dx/merge/TypeList.java b/dx/src/com/android/jack/dx/merge/TypeList.java index a34d000..0c5b4f9 100644 --- a/dx/src/com/android/jack/dx/merge/TypeList.java +++ b/dx/src/com/android/jack/dx/merge/TypeList.java @@ -19,40 +19,43 @@ package com.android.jack.dx.merge; import com.android.jack.dx.io.DexBuffer; import com.android.jack.dx.util.Unsigned; -import java.util.Arrays; - +/** + * TODO(jack team) + */ public final class TypeList implements Comparable<TypeList> { - public static final TypeList EMPTY = new TypeList(null, new short[0]); + public static final TypeList EMPTY = new TypeList(null, new short[0]); - private final DexBuffer buffer; - private final short[] types; + private final DexBuffer buffer; + private final short[] types; - public TypeList(DexBuffer buffer, short[] types) { - this.buffer = buffer; - this.types = types; - } + public TypeList(DexBuffer buffer, short[] types) { + this.buffer = buffer; + this.types = types; + } - public short[] getTypes() { - return types; - } + public short[] getTypes() { + return types; + } - public int compareTo(TypeList other) { - for (int i = 0; i < types.length && i < other.types.length; i++) { - if (types[i] != other.types[i]) { - return Unsigned.compare(types[i], other.types[i]); - } - } - return Unsigned.compare(types.length, other.types.length); + @Override + public int compareTo(TypeList other) { + for (int i = 0; i < types.length && i < other.types.length; i++) { + if (types[i] != other.types[i]) { + return Unsigned.compare(types[i], other.types[i]); + } } - - @Override public String toString() { - StringBuilder result = new StringBuilder(); - result.append("("); - for (int i = 0, typesLength = types.length; i < typesLength; i++) { - result.append(buffer != null ? buffer.typeNames().get(types[i]) : types[i]); - } - result.append(")"); - return result.toString(); + return Unsigned.compare(types.length, other.types.length); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("("); + for (int i = 0, typesLength = types.length; i < typesLength; i++) { + result.append(buffer != null ? buffer.typeNames().get(types[i]) : types[i]); } + result.append(")"); + return result.toString(); + } } diff --git a/dx/src/com/android/jack/dx/rop/annotation/Annotation.java b/dx/src/com/android/jack/dx/rop/annotation/Annotation.java index ccf331e..a0453e8 100644 --- a/dx/src/com/android/jack/dx/rop/annotation/Annotation.java +++ b/dx/src/com/android/jack/dx/rop/annotation/Annotation.java @@ -31,194 +31,195 @@ import java.util.TreeMap; * associated type and additionally consist of a set of (name, value) * pairs, where the names are unique. */ -public final class Annotation extends MutabilityControl - implements Comparable<Annotation>, ToHuman { - /** {@code non-null;} type of the annotation */ - private final CstType type; - - /** {@code non-null;} the visibility of the annotation */ - private final AnnotationVisibility visibility; - - /** {@code non-null;} map from names to {@link NameValuePair} instances */ - private final TreeMap<CstString, NameValuePair> elements; - - /** - * Construct an instance. It initially contains no elements. - * - * @param type {@code non-null;} type of the annotation - * @param visibility {@code non-null;} the visibility of the annotation - */ - public Annotation(CstType type, AnnotationVisibility visibility) { - if (type == null) { - throw new NullPointerException("type == null"); - } - - if (visibility == null) { - throw new NullPointerException("visibility == null"); - } - - this.type = type; - this.visibility = visibility; - this.elements = new TreeMap<CstString, NameValuePair>(); +public final class Annotation extends MutabilityControl implements Comparable<Annotation>, ToHuman { + /** {@code non-null;} type of the annotation */ + private final CstType type; + + /** {@code non-null;} the visibility of the annotation */ + private final AnnotationVisibility visibility; + + /** {@code non-null;} map from names to {@link NameValuePair} instances */ + private final TreeMap<CstString, NameValuePair> elements; + + /** + * Construct an instance. It initially contains no elements. + * + * @param type {@code non-null;} type of the annotation + * @param visibility {@code non-null;} the visibility of the annotation + */ + public Annotation(CstType type, AnnotationVisibility visibility) { + if (type == null) { + throw new NullPointerException("type == null"); } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (! (other instanceof Annotation)) { - return false; - } - - Annotation otherAnnotation = (Annotation) other; - - if (! (type.equals(otherAnnotation.type) - && (visibility == otherAnnotation.visibility))) { - return false; - } - - return elements.equals(otherAnnotation.elements); + if (visibility == null) { + throw new NullPointerException("visibility == null"); } - /** {@inheritDoc} */ - public int hashCode() { - int hash = type.hashCode(); - hash = (hash * 31) + elements.hashCode(); - hash = (hash * 31) + visibility.hashCode(); - return hash; - } + this.type = type; + this.visibility = visibility; + this.elements = new TreeMap<CstString, NameValuePair>(); + } - /** {@inheritDoc} */ - public int compareTo(Annotation other) { - int result = type.compareTo(other.type); - - if (result != 0) { - return result; - } + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof Annotation)) { + return false; + } - result = visibility.compareTo(other.visibility); + Annotation otherAnnotation = (Annotation) other; - if (result != 0) { - return result; - } + if (!(type.equals(otherAnnotation.type) && (visibility == otherAnnotation.visibility))) { + return false; + } - Iterator<NameValuePair> thisIter = elements.values().iterator(); - Iterator<NameValuePair> otherIter = other.elements.values().iterator(); + return elements.equals(otherAnnotation.elements); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + int hash = type.hashCode(); + hash = (hash * 31) + elements.hashCode(); + hash = (hash * 31) + visibility.hashCode(); + return hash; + } + + /** {@inheritDoc} */ + @Override + public int compareTo(Annotation other) { + int result = type.compareTo(other.type); + + if (result != 0) { + return result; + } - while (thisIter.hasNext() && otherIter.hasNext()) { - NameValuePair thisOne = thisIter.next(); - NameValuePair otherOne = otherIter.next(); + result = visibility.compareTo(other.visibility); - result = thisOne.compareTo(otherOne); - if (result != 0) { - return result; - } - } + if (result != 0) { + return result; + } - if (thisIter.hasNext()) { - return 1; - } else if (otherIter.hasNext()) { - return -1; - } + Iterator<NameValuePair> thisIter = elements.values().iterator(); + Iterator<NameValuePair> otherIter = other.elements.values().iterator(); - return 0; - } + while (thisIter.hasNext() && otherIter.hasNext()) { + NameValuePair thisOne = thisIter.next(); + NameValuePair otherOne = otherIter.next(); - /** {@inheritDoc} */ - @Override - public String toString() { - return toHuman(); + result = thisOne.compareTo(otherOne); + if (result != 0) { + return result; + } } - /** {@inheritDoc} */ - public String toHuman() { - StringBuilder sb = new StringBuilder(); - - sb.append(visibility.toHuman()); - sb.append("-annotation "); - sb.append(type.toHuman()); - sb.append(" {"); - - boolean first = true; - for (NameValuePair pair : elements.values()) { - if (first) { - first = false; - } else { - sb.append(", "); - } - sb.append(pair.getName().toHuman()); - sb.append(": "); - sb.append(pair.getValue().toHuman()); - } - - sb.append("}"); - return sb.toString(); + if (thisIter.hasNext()) { + return 1; + } else if (otherIter.hasNext()) { + return -1; } - /** - * Gets the type of this instance. - * - * @return {@code non-null;} the type - */ - public CstType getType() { - return type; + return 0; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return toHuman(); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + StringBuilder sb = new StringBuilder(); + + sb.append(visibility.toHuman()); + sb.append("-annotation "); + sb.append(type.toHuman()); + sb.append(" {"); + + boolean first = true; + for (NameValuePair pair : elements.values()) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append(pair.getName().toHuman()); + sb.append(": "); + sb.append(pair.getValue().toHuman()); } - /** - * Gets the visibility of this instance. - * - * @return {@code non-null;} the visibility - */ - public AnnotationVisibility getVisibility() { - return visibility; + sb.append("}"); + return sb.toString(); + } + + /** + * Gets the type of this instance. + * + * @return {@code non-null;} the type + */ + public CstType getType() { + return type; + } + + /** + * Gets the visibility of this instance. + * + * @return {@code non-null;} the visibility + */ + public AnnotationVisibility getVisibility() { + return visibility; + } + + /** + * Put an element into the set of (name, value) pairs for this instance. + * If there is a preexisting element with the same name, it will be + * replaced by this method. + * + * @param pair {@code non-null;} the (name, value) pair to place into this instance + */ + public void put(NameValuePair pair) { + throwIfImmutable(); + + if (pair == null) { + throw new NullPointerException("pair == null"); } - /** - * Put an element into the set of (name, value) pairs for this instance. - * If there is a preexisting element with the same name, it will be - * replaced by this method. - * - * @param pair {@code non-null;} the (name, value) pair to place into this instance - */ - public void put(NameValuePair pair) { - throwIfImmutable(); - - if (pair == null) { - throw new NullPointerException("pair == null"); - } - - elements.put(pair.getName(), pair); + elements.put(pair.getName(), pair); + } + + /** + * Add an element to the set of (name, value) pairs for this instance. + * It is an error to call this method if there is a preexisting element + * with the same name. + * + * @param pair {@code non-null;} the (name, value) pair to add to this instance + */ + public void add(NameValuePair pair) { + throwIfImmutable(); + + if (pair == null) { + throw new NullPointerException("pair == null"); } - /** - * Add an element to the set of (name, value) pairs for this instance. - * It is an error to call this method if there is a preexisting element - * with the same name. - * - * @param pair {@code non-null;} the (name, value) pair to add to this instance - */ - public void add(NameValuePair pair) { - throwIfImmutable(); - - if (pair == null) { - throw new NullPointerException("pair == null"); - } + CstString name = pair.getName(); - CstString name = pair.getName(); - - if (elements.get(name) != null) { - throw new IllegalArgumentException("name already added: " + name); - } - - elements.put(name, pair); + if (elements.get(name) != null) { + throw new IllegalArgumentException("name already added: " + name); } - /** - * Gets the set of name-value pairs contained in this instance. The - * result is always unmodifiable. - * - * @return {@code non-null;} the set of name-value pairs - */ - public Collection<NameValuePair> getNameValuePairs() { - return Collections.unmodifiableCollection(elements.values()); - } + elements.put(name, pair); + } + + /** + * Gets the set of name-value pairs contained in this instance. The + * result is always unmodifiable. + * + * @return {@code non-null;} the set of name-value pairs + */ + public Collection<NameValuePair> getNameValuePairs() { + return Collections.unmodifiableCollection(elements.values()); + } } diff --git a/dx/src/com/android/jack/dx/rop/annotation/AnnotationVisibility.java b/dx/src/com/android/jack/dx/rop/annotation/AnnotationVisibility.java index e8ca504..874de4e 100644 --- a/dx/src/com/android/jack/dx/rop/annotation/AnnotationVisibility.java +++ b/dx/src/com/android/jack/dx/rop/annotation/AnnotationVisibility.java @@ -22,25 +22,23 @@ import com.android.jack.dx.util.ToHuman; * Visibility scope of an annotation. */ public enum AnnotationVisibility implements ToHuman { - RUNTIME("runtime"), - BUILD("build"), - SYSTEM("system"), - EMBEDDED("embedded"); + RUNTIME("runtime"), BUILD("build"), SYSTEM("system"), EMBEDDED("embedded"); - /** {@code non-null;} the human-oriented string representation */ - private final String human; + /** {@code non-null;} the human-oriented string representation */ + private final String human; - /** - * Constructs an instance. - * - * @param human {@code non-null;} the human-oriented string representation - */ - private AnnotationVisibility(String human) { - this.human = human; - } + /** + * Constructs an instance. + * + * @param human {@code non-null;} the human-oriented string representation + */ + private AnnotationVisibility(String human) { + this.human = human; + } - /** {@inheritDoc} */ - public String toHuman() { - return human; - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return human; + } } diff --git a/dx/src/com/android/jack/dx/rop/annotation/Annotations.java b/dx/src/com/android/jack/dx/rop/annotation/Annotations.java index 9f9f92b..626f963 100644 --- a/dx/src/com/android/jack/dx/rop/annotation/Annotations.java +++ b/dx/src/com/android/jack/dx/rop/annotation/Annotations.java @@ -27,187 +27,186 @@ import java.util.TreeMap; /** * List of {@link Annotation} instances. */ -public final class Annotations extends MutabilityControl - implements Comparable<Annotations> { - /** {@code non-null;} immutable empty instance */ - public static final Annotations EMPTY = new Annotations(); - - static { - EMPTY.setImmutable(); - } - - /** {@code non-null;} map from types to annotations */ - private final TreeMap<CstType, Annotation> annotations; - - /** - * Constructs an immutable instance which is the combination of the - * two given instances. The two instances must contain disjoint sets - * of types. - * - * @param a1 {@code non-null;} an instance - * @param a2 {@code non-null;} the other instance - * @return {@code non-null;} the combination - * @throws IllegalArgumentException thrown if there is a duplicate type - */ - public static Annotations combine(Annotations a1, Annotations a2) { - Annotations result = new Annotations(); - - result.addAll(a1); - result.addAll(a2); - result.setImmutable(); - - return result; +public final class Annotations extends MutabilityControl implements Comparable<Annotations> { + /** {@code non-null;} immutable empty instance */ + public static final Annotations EMPTY = new Annotations(); + + static { + EMPTY.setImmutable(); + } + + /** {@code non-null;} map from types to annotations */ + private final TreeMap<CstType, Annotation> annotations; + + /** + * Constructs an immutable instance which is the combination of the + * two given instances. The two instances must contain disjoint sets + * of types. + * + * @param a1 {@code non-null;} an instance + * @param a2 {@code non-null;} the other instance + * @return {@code non-null;} the combination + * @throws IllegalArgumentException thrown if there is a duplicate type + */ + public static Annotations combine(Annotations a1, Annotations a2) { + Annotations result = new Annotations(); + + result.addAll(a1); + result.addAll(a2); + result.setImmutable(); + + return result; + } + + /** + * Constructs an immutable instance which is the combination of the + * given instance with the given additional annotation. The latter's + * type must not already appear in the former. + * + * @param annotations {@code non-null;} the instance to augment + * @param annotation {@code non-null;} the additional annotation + * @return {@code non-null;} the combination + * @throws IllegalArgumentException thrown if there is a duplicate type + */ + public static Annotations combine(Annotations annotations, Annotation annotation) { + Annotations result = new Annotations(); + + result.addAll(annotations); + result.add(annotation); + result.setImmutable(); + + return result; + } + + /** + * Constructs an empty instance. + */ + public Annotations() { + annotations = new TreeMap<CstType, Annotation>(); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return annotations.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof Annotations)) { + return false; } - /** - * Constructs an immutable instance which is the combination of the - * given instance with the given additional annotation. The latter's - * type must not already appear in the former. - * - * @param annotations {@code non-null;} the instance to augment - * @param annotation {@code non-null;} the additional annotation - * @return {@code non-null;} the combination - * @throws IllegalArgumentException thrown if there is a duplicate type - */ - public static Annotations combine(Annotations annotations, - Annotation annotation) { - Annotations result = new Annotations(); - - result.addAll(annotations); - result.add(annotation); - result.setImmutable(); + Annotations otherAnnotations = (Annotations) other; - return result; - } + return annotations.equals(otherAnnotations.annotations); + } - /** - * Constructs an empty instance. - */ - public Annotations() { - annotations = new TreeMap<CstType, Annotation>(); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return annotations.hashCode(); - } + /** {@inheritDoc} */ + @Override + public int compareTo(Annotations other) { + Iterator<Annotation> thisIter = annotations.values().iterator(); + Iterator<Annotation> otherIter = other.annotations.values().iterator(); - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (! (other instanceof Annotations)) { - return false; - } + while (thisIter.hasNext() && otherIter.hasNext()) { + Annotation thisOne = thisIter.next(); + Annotation otherOne = otherIter.next(); - Annotations otherAnnotations = (Annotations) other; - - return annotations.equals(otherAnnotations.annotations); + int result = thisOne.compareTo(otherOne); + if (result != 0) { + return result; + } } - /** {@inheritDoc} */ - public int compareTo(Annotations other) { - Iterator<Annotation> thisIter = annotations.values().iterator(); - Iterator<Annotation> otherIter = other.annotations.values().iterator(); - - while (thisIter.hasNext() && otherIter.hasNext()) { - Annotation thisOne = thisIter.next(); - Annotation otherOne = otherIter.next(); - - int result = thisOne.compareTo(otherOne); - if (result != 0) { - return result; - } - } - - if (thisIter.hasNext()) { - return 1; - } else if (otherIter.hasNext()) { - return -1; - } - - return 0; + if (thisIter.hasNext()) { + return 1; + } else if (otherIter.hasNext()) { + return -1; } - /** {@inheritDoc} */ - public String toString() { - StringBuilder sb = new StringBuilder(); - boolean first = true; + return 0; + } - sb.append("annotations{"); + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + boolean first = true; - for (Annotation a : annotations.values()) { - if (first) { - first = false; - } else { - sb.append(", "); - } - sb.append(a.toHuman()); - } + sb.append("annotations{"); - sb.append("}"); - return sb.toString(); + for (Annotation a : annotations.values()) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append(a.toHuman()); } - /** - * Gets the number of elements in this instance. - * - * @return {@code >= 0;} the size - */ - public int size() { - return annotations.size(); + sb.append("}"); + return sb.toString(); + } + + /** + * Gets the number of elements in this instance. + * + * @return {@code >= 0;} the size + */ + public int size() { + return annotations.size(); + } + + /** + * Adds an element to this instance. There must not already be an + * element of the same type. + * + * @param annotation {@code non-null;} the element to add + * @throws IllegalArgumentException thrown if there is a duplicate type + */ + public void add(Annotation annotation) { + throwIfImmutable(); + + if (annotation == null) { + throw new NullPointerException("annotation == null"); } - /** - * Adds an element to this instance. There must not already be an - * element of the same type. - * - * @param annotation {@code non-null;} the element to add - * @throws IllegalArgumentException thrown if there is a duplicate type - */ - public void add(Annotation annotation) { - throwIfImmutable(); - - if (annotation == null) { - throw new NullPointerException("annotation == null"); - } - - CstType type = annotation.getType(); - - if (annotations.containsKey(type)) { - throw new IllegalArgumentException("duplicate type: " + - type.toHuman()); - } + CstType type = annotation.getType(); - annotations.put(type, annotation); + if (annotations.containsKey(type)) { + throw new IllegalArgumentException("duplicate type: " + type.toHuman()); } - /** - * Adds all of the elements of the given instance to this one. The - * instances must not have any duplicate types. - * - * @param toAdd {@code non-null;} the annotations to add - * @throws IllegalArgumentException thrown if there is a duplicate type - */ - public void addAll(Annotations toAdd) { - throwIfImmutable(); - - if (toAdd == null) { - throw new NullPointerException("toAdd == null"); - } - - for (Annotation a : toAdd.annotations.values()) { - add(a); - } + annotations.put(type, annotation); + } + + /** + * Adds all of the elements of the given instance to this one. The + * instances must not have any duplicate types. + * + * @param toAdd {@code non-null;} the annotations to add + * @throws IllegalArgumentException thrown if there is a duplicate type + */ + public void addAll(Annotations toAdd) { + throwIfImmutable(); + + if (toAdd == null) { + throw new NullPointerException("toAdd == null"); } - /** - * Gets the set of annotations contained in this instance. The - * result is always unmodifiable. - * - * @return {@code non-null;} the set of annotations - */ - public Collection<Annotation> getAnnotations() { - return Collections.unmodifiableCollection(annotations.values()); + for (Annotation a : toAdd.annotations.values()) { + add(a); } + } + + /** + * Gets the set of annotations contained in this instance. The + * result is always unmodifiable. + * + * @return {@code non-null;} the set of annotations + */ + public Collection<Annotation> getAnnotations() { + return Collections.unmodifiableCollection(annotations.values()); + } } diff --git a/dx/src/com/android/jack/dx/rop/annotation/AnnotationsList.java b/dx/src/com/android/jack/dx/rop/annotation/AnnotationsList.java index a013673..dc0522f 100644 --- a/dx/src/com/android/jack/dx/rop/annotation/AnnotationsList.java +++ b/dx/src/com/android/jack/dx/rop/annotation/AnnotationsList.java @@ -21,71 +21,69 @@ import com.android.jack.dx.util.FixedSizeList; /** * List of {@link Annotations} instances. */ -public final class AnnotationsList - extends FixedSizeList { - /** {@code non-null;} immutable empty instance */ - public static final AnnotationsList EMPTY = new AnnotationsList(0); +public final class AnnotationsList extends FixedSizeList { + /** {@code non-null;} immutable empty instance */ + public static final AnnotationsList EMPTY = new AnnotationsList(0); - /** - * Constructs an immutable instance which is the combination of - * the two given instances. The two instances must each have the - * same number of elements, and each pair of elements must contain - * disjoint sets of types. - * - * @param list1 {@code non-null;} an instance - * @param list2 {@code non-null;} the other instance - * @return {@code non-null;} the combination - */ - public static AnnotationsList combine(AnnotationsList list1, - AnnotationsList list2) { - int size = list1.size(); + /** + * Constructs an immutable instance which is the combination of + * the two given instances. The two instances must each have the + * same number of elements, and each pair of elements must contain + * disjoint sets of types. + * + * @param list1 {@code non-null;} an instance + * @param list2 {@code non-null;} the other instance + * @return {@code non-null;} the combination + */ + public static AnnotationsList combine(AnnotationsList list1, AnnotationsList list2) { + int size = list1.size(); - if (size != list2.size()) { - throw new IllegalArgumentException("list1.size() != list2.size()"); - } - - AnnotationsList result = new AnnotationsList(size); + if (size != list2.size()) { + throw new IllegalArgumentException("list1.size() != list2.size()"); + } - for (int i = 0; i < size; i++) { - Annotations a1 = list1.get(i); - Annotations a2 = list2.get(i); - result.set(i, Annotations.combine(a1, a2)); - } + AnnotationsList result = new AnnotationsList(size); - result.setImmutable(); - return result; + for (int i = 0; i < size; i++) { + Annotations a1 = list1.get(i); + Annotations a2 = list2.get(i); + result.set(i, Annotations.combine(a1, a2)); } - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size the size of the list - */ - public AnnotationsList(int size) { - super(size); - } + result.setImmutable(); + return result; + } - /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index - */ - public Annotations get(int n) { - return (Annotations) get0(n); - } + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size the size of the list + */ + public AnnotationsList(int size) { + super(size); + } - /** - * Sets the element at the given index. The given element must be - * immutable. - * - * @param n {@code >= 0, < size();} which index - * @param a {@code null-ok;} the element to set at {@code n} - */ - public void set(int n, Annotations a) { - a.throwIfMutable(); - set0(n, a); - } + /** + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index + */ + public Annotations get(int n) { + return (Annotations) get0(n); + } + + /** + * Sets the element at the given index. The given element must be + * immutable. + * + * @param n {@code >= 0, < size();} which index + * @param a {@code null-ok;} the element to set at {@code n} + */ + public void set(int n, Annotations a) { + a.throwIfMutable(); + set0(n, a); + } } diff --git a/dx/src/com/android/jack/dx/rop/annotation/NameValuePair.java b/dx/src/com/android/jack/dx/rop/annotation/NameValuePair.java index 7661fbf..018e299 100644 --- a/dx/src/com/android/jack/dx/rop/annotation/NameValuePair.java +++ b/dx/src/com/android/jack/dx/rop/annotation/NameValuePair.java @@ -23,84 +23,87 @@ import com.android.jack.dx.rop.cst.CstString; * A (name, value) pair. These are used as the contents of an annotation. */ public final class NameValuePair implements Comparable<NameValuePair> { - /** {@code non-null;} the name */ - private final CstString name; - - /** {@code non-null;} the value */ - private final Constant value; - - /** - * Construct an instance. - * - * @param name {@code non-null;} the name - * @param value {@code non-null;} the value - */ - public NameValuePair(CstString name, Constant value) { - if (name == null) { - throw new NullPointerException("name == null"); - } - - if (value == null) { - throw new NullPointerException("value == null"); - } - - this.name = name; - this.value = value; + /** {@code non-null;} the name */ + private final CstString name; + + /** {@code non-null;} the value */ + private final Constant value; + + /** + * Construct an instance. + * + * @param name {@code non-null;} the name + * @param value {@code non-null;} the value + */ + public NameValuePair(CstString name, Constant value) { + if (name == null) { + throw new NullPointerException("name == null"); } - /** {@inheritDoc} */ - public String toString() { - return name.toHuman() + ":" + value; + if (value == null) { + throw new NullPointerException("value == null"); } - /** {@inheritDoc} */ - public int hashCode() { - return name.hashCode() * 31 + value.hashCode(); + this.name = name; + this.value = value; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return name.toHuman() + ":" + value; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return name.hashCode() * 31 + value.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof NameValuePair)) { + return false; } - /** {@inheritDoc} */ - public boolean equals(Object other) { - if (! (other instanceof NameValuePair)) { - return false; - } + NameValuePair otherPair = (NameValuePair) other; - NameValuePair otherPair = (NameValuePair) other; + return name.equals(otherPair.name) && value.equals(otherPair.value); + } - return name.equals(otherPair.name) - && value.equals(otherPair.value); - } - - /** - * {@inheritDoc} - * - * <p>Instances of this class compare in name-major and value-minor - * order.</p> - */ - public int compareTo(NameValuePair other) { - int result = name.compareTo(other.name); - - if (result != 0) { - return result; - } + /** + * {@inheritDoc} + * + * <p>Instances of this class compare in name-major and value-minor + * order.</p> + */ + @Override + public int compareTo(NameValuePair other) { + int result = name.compareTo(other.name); - return value.compareTo(other.value); + if (result != 0) { + return result; } - /** - * Gets the name. - * - * @return {@code non-null;} the name - */ - public CstString getName() { - return name; - } - - /** - * Gets the value. - * - * @return {@code non-null;} the value - */ - public Constant getValue() { - return value; - } + return value.compareTo(other.value); + } + + /** + * Gets the name. + * + * @return {@code non-null;} the name + */ + public CstString getName() { + return name; + } + + /** + * Gets the value. + * + * @return {@code non-null;} the value + */ + public Constant getValue() { + return value; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/AccessFlags.java b/dx/src/com/android/jack/dx/rop/code/AccessFlags.java index 95385bd..7dc3a80 100644 --- a/dx/src/com/android/jack/dx/rop/code/AccessFlags.java +++ b/dx/src/com/android/jack/dx/rop/code/AccessFlags.java @@ -28,379 +28,373 @@ import com.android.jack.dx.util.Hex; * is only used in a very limited way. */ public final class AccessFlags { - /** public member / class */ - public static final int ACC_PUBLIC = 0x0001; + /** public member / class */ + public static final int ACC_PUBLIC = 0x0001; - /** private member */ - public static final int ACC_PRIVATE = 0x0002; + /** private member */ + public static final int ACC_PRIVATE = 0x0002; - /** protected member */ - public static final int ACC_PROTECTED = 0x0004; + /** protected member */ + public static final int ACC_PROTECTED = 0x0004; - /** static member */ - public static final int ACC_STATIC = 0x0008; - - /** final member / class */ - public static final int ACC_FINAL = 0x0010; - - /** - * synchronized method; only valid in dex files for {@code native} - * methods - */ - public static final int ACC_SYNCHRONIZED = 0x0020; - - /** - * class with new-style {@code invokespecial} for superclass - * method access - */ - public static final int ACC_SUPER = 0x0020; - - /** volatile field */ - public static final int ACC_VOLATILE = 0x0040; - - /** bridge method (generated) */ - public static final int ACC_BRIDGE = 0x0040; - - /** transient field */ - public static final int ACC_TRANSIENT = 0x0080; - - /** varargs method */ - public static final int ACC_VARARGS = 0x0080; - - /** native method */ - public static final int ACC_NATIVE = 0x0100; - - /** "class" is in fact an public static final interface */ - public static final int ACC_INTERFACE = 0x0200; - - /** abstract method / class */ - public static final int ACC_ABSTRACT = 0x0400; - - /** - * method with strict floating point ({@code strictfp}) - * behavior - */ - public static final int ACC_STRICT = 0x0800; - - /** synthetic member */ - public static final int ACC_SYNTHETIC = 0x1000; - - /** class is an annotation type */ - public static final int ACC_ANNOTATION = 0x2000; - - /** - * class is an enumerated type; field is an element of an enumerated - * type - */ - public static final int ACC_ENUM = 0x4000; - - /** method is a constructor */ - public static final int ACC_CONSTRUCTOR = 0x10000; - - /** - * method was declared {@code synchronized}; has no effect on - * execution (other than inspecting this flag, per se) - */ - public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000; - - /** flags defined on classes */ - public static final int CLASS_FLAGS = - ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT | - ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM; - - /** flags defined on inner classes */ - public static final int INNER_CLASS_FLAGS = - ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | - ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | - ACC_ENUM; - - /** flags defined on fields */ - public static final int FIELD_FLAGS = - ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | - ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM; - - /** flags defined on methods */ - public static final int METHOD_FLAGS = - ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | - ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | - ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR | - ACC_DECLARED_SYNCHRONIZED; - - /** indicates conversion of class flags */ - private static final int CONV_CLASS = 1; - - /** indicates conversion of field flags */ - private static final int CONV_FIELD = 2; - - /** indicates conversion of method flags */ - private static final int CONV_METHOD = 3; - - /** - * This class is uninstantiable. - */ - private AccessFlags() { - // This space intentionally left blank. - } + /** static member */ + public static final int ACC_STATIC = 0x0008; - /** - * Returns a human-oriented string representing the given access flags, - * as defined on classes (not fields or methods). - * - * @param flags the flags - * @return {@code non-null;} human-oriented string - */ - public static String classString(int flags) { - return humanHelper(flags, CLASS_FLAGS, CONV_CLASS); - } + /** final member / class */ + public static final int ACC_FINAL = 0x0010; - /** - * Returns a human-oriented string representing the given access flags, - * as defined on inner classes. - * - * @param flags the flags - * @return {@code non-null;} human-oriented string - */ - public static String innerClassString(int flags) { - return humanHelper(flags, INNER_CLASS_FLAGS, CONV_CLASS); + /** + * synchronized method; only valid in dex files for {@code native} + * methods + */ + public static final int ACC_SYNCHRONIZED = 0x0020; + + /** + * class with new-style {@code invokespecial} for superclass + * method access + */ + public static final int ACC_SUPER = 0x0020; + + /** volatile field */ + public static final int ACC_VOLATILE = 0x0040; + + /** bridge method (generated) */ + public static final int ACC_BRIDGE = 0x0040; + + /** transient field */ + public static final int ACC_TRANSIENT = 0x0080; + + /** varargs method */ + public static final int ACC_VARARGS = 0x0080; + + /** native method */ + public static final int ACC_NATIVE = 0x0100; + + /** "class" is in fact an public static final interface */ + public static final int ACC_INTERFACE = 0x0200; + + /** abstract method / class */ + public static final int ACC_ABSTRACT = 0x0400; + + /** + * method with strict floating point ({@code strictfp}) + * behavior + */ + public static final int ACC_STRICT = 0x0800; + + /** synthetic member */ + public static final int ACC_SYNTHETIC = 0x1000; + + /** class is an annotation type */ + public static final int ACC_ANNOTATION = 0x2000; + + /** + * class is an enumerated type; field is an element of an enumerated + * type + */ + public static final int ACC_ENUM = 0x4000; + + /** method is a constructor */ + public static final int ACC_CONSTRUCTOR = 0x10000; + + /** + * method was declared {@code synchronized}; has no effect on + * execution (other than inspecting this flag, per se) + */ + public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000; + + /** flags defined on classes */ + public static final int CLASS_FLAGS = ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE + | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM; + + /** flags defined on inner classes */ + public static final int INNER_CLASS_FLAGS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC + | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM; + + /** flags defined on fields */ + public static final int FIELD_FLAGS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC + | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM; + + /** flags defined on methods */ + public static final int METHOD_FLAGS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC + | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT + | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR | ACC_DECLARED_SYNCHRONIZED; + + /** indicates conversion of class flags */ + private static final int CONV_CLASS = 1; + + /** indicates conversion of field flags */ + private static final int CONV_FIELD = 2; + + /** indicates conversion of method flags */ + private static final int CONV_METHOD = 3; + + /** + * This class is uninstantiable. + */ + private AccessFlags() { + // This space intentionally left blank. + } + + /** + * Returns a human-oriented string representing the given access flags, + * as defined on classes (not fields or methods). + * + * @param flags the flags + * @return {@code non-null;} human-oriented string + */ + public static String classString(int flags) { + return humanHelper(flags, CLASS_FLAGS, CONV_CLASS); + } + + /** + * Returns a human-oriented string representing the given access flags, + * as defined on inner classes. + * + * @param flags the flags + * @return {@code non-null;} human-oriented string + */ + public static String innerClassString(int flags) { + return humanHelper(flags, INNER_CLASS_FLAGS, CONV_CLASS); + } + + /** + * Returns a human-oriented string representing the given access flags, + * as defined on fields (not classes or methods). + * + * @param flags the flags + * @return {@code non-null;} human-oriented string + */ + public static String fieldString(int flags) { + return humanHelper(flags, FIELD_FLAGS, CONV_FIELD); + } + + /** + * Returns a human-oriented string representing the given access flags, + * as defined on methods (not classes or fields). + * + * @param flags the flags + * @return {@code non-null;} human-oriented string + */ + public static String methodString(int flags) { + return humanHelper(flags, METHOD_FLAGS, CONV_METHOD); + } + + /** + * Returns whether the flag {@code ACC_PUBLIC} is on in the given + * flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_PUBLIC} flag + */ + public static boolean isPublic(int flags) { + return (flags & ACC_PUBLIC) != 0; + } + + /** + * Returns whether the flag {@code ACC_PROTECTED} is on in the given + * flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_PROTECTED} flag + */ + public static boolean isProtected(int flags) { + return (flags & ACC_PROTECTED) != 0; + } + + /** + * Returns whether the flag {@code ACC_PRIVATE} is on in the given + * flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_PRIVATE} flag + */ + public static boolean isPrivate(int flags) { + return (flags & ACC_PRIVATE) != 0; + } + + /** + * Returns whether the flag {@code ACC_STATIC} is on in the given + * flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_STATIC} flag + */ + public static boolean isStatic(int flags) { + return (flags & ACC_STATIC) != 0; + } + + /** + * Returns whether the flag {@code ACC_CONSTRUCTOR} is on in + * the given flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_CONSTRUCTOR} flag + */ + public static boolean isConstructor(int flags) { + return (flags & ACC_CONSTRUCTOR) != 0; + } + + /** + * Returns whether the flag {@code ACC_INTERFACE} is on in + * the given flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_INTERFACE} flag + */ + public static boolean isInterface(int flags) { + return (flags & ACC_INTERFACE) != 0; + } + + /** + * Returns whether the flag {@code ACC_SYNCHRONIZED} is on in + * the given flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_SYNCHRONIZED} flag + */ + public static boolean isSynchronized(int flags) { + return (flags & ACC_SYNCHRONIZED) != 0; + } + + /** + * Returns whether the flag {@code ACC_ABSTRACT} is on in the given + * flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_ABSTRACT} flag + */ + public static boolean isAbstract(int flags) { + return (flags & ACC_ABSTRACT) != 0; + } + + /** + * Returns whether the flag {@code ACC_NATIVE} is on in the given + * flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_NATIVE} flag + */ + public static boolean isNative(int flags) { + return (flags & ACC_NATIVE) != 0; + } + + /** + * Returns whether the flag {@code ACC_ANNOTATION} is on in the given + * flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_ANNOTATION} flag + */ + public static boolean isAnnotation(int flags) { + return (flags & ACC_ANNOTATION) != 0; + } + + /** + * Returns whether the flag {@code ACC_DECLARED_SYNCHRONIZED} is + * on in the given flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_DECLARED_SYNCHRONIZED} flag + */ + public static boolean isDeclaredSynchronized(int flags) { + return (flags & ACC_DECLARED_SYNCHRONIZED) != 0; + } + + /** + * Returns whether the flag {@code ACC_ENUM} is on in the given flags. + * + * @param flags the flags to check + * @return the value of the {@code ACC_ENUM} flag + */ + public static boolean isEnum(int flags) { + return (flags & ACC_ENUM) != 0; + } + + /** + * Helper to return a human-oriented string representing the given + * access flags. + * + * @param flags the defined flags + * @param mask mask for the "defined" bits + * @param what what the flags represent (one of {@code CONV_*}) + * @return {@code non-null;} human-oriented string + */ + private static String humanHelper(int flags, int mask, int what) { + StringBuffer sb = new StringBuffer(80); + int extra = flags & ~mask; + + flags &= mask; + + if ((flags & ACC_PUBLIC) != 0) { + sb.append("|public"); } - - /** - * Returns a human-oriented string representing the given access flags, - * as defined on fields (not classes or methods). - * - * @param flags the flags - * @return {@code non-null;} human-oriented string - */ - public static String fieldString(int flags) { - return humanHelper(flags, FIELD_FLAGS, CONV_FIELD); + if ((flags & ACC_PRIVATE) != 0) { + sb.append("|private"); } - - /** - * Returns a human-oriented string representing the given access flags, - * as defined on methods (not classes or fields). - * - * @param flags the flags - * @return {@code non-null;} human-oriented string - */ - public static String methodString(int flags) { - return humanHelper(flags, METHOD_FLAGS, CONV_METHOD); + if ((flags & ACC_PROTECTED) != 0) { + sb.append("|protected"); } - - /** - * Returns whether the flag {@code ACC_PUBLIC} is on in the given - * flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_PUBLIC} flag - */ - public static boolean isPublic(int flags) { - return (flags & ACC_PUBLIC) != 0; + if ((flags & ACC_STATIC) != 0) { + sb.append("|static"); } - - /** - * Returns whether the flag {@code ACC_PROTECTED} is on in the given - * flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_PROTECTED} flag - */ - public static boolean isProtected(int flags) { - return (flags & ACC_PROTECTED) != 0; + if ((flags & ACC_FINAL) != 0) { + sb.append("|final"); } - - /** - * Returns whether the flag {@code ACC_PRIVATE} is on in the given - * flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_PRIVATE} flag - */ - public static boolean isPrivate(int flags) { - return (flags & ACC_PRIVATE) != 0; + if ((flags & ACC_SYNCHRONIZED) != 0) { + if (what == CONV_CLASS) { + sb.append("|super"); + } else { + sb.append("|synchronized"); + } } - - /** - * Returns whether the flag {@code ACC_STATIC} is on in the given - * flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_STATIC} flag - */ - public static boolean isStatic(int flags) { - return (flags & ACC_STATIC) != 0; + if ((flags & ACC_VOLATILE) != 0) { + if (what == CONV_METHOD) { + sb.append("|bridge"); + } else { + sb.append("|volatile"); + } } - - /** - * Returns whether the flag {@code ACC_CONSTRUCTOR} is on in - * the given flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_CONSTRUCTOR} flag - */ - public static boolean isConstructor(int flags) { - return (flags & ACC_CONSTRUCTOR) != 0; + if ((flags & ACC_TRANSIENT) != 0) { + if (what == CONV_METHOD) { + sb.append("|varargs"); + } else { + sb.append("|transient"); + } } - - /** - * Returns whether the flag {@code ACC_INTERFACE} is on in - * the given flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_INTERFACE} flag - */ - public static boolean isInterface(int flags) { - return (flags & ACC_INTERFACE) != 0; + if ((flags & ACC_NATIVE) != 0) { + sb.append("|native"); } - - /** - * Returns whether the flag {@code ACC_SYNCHRONIZED} is on in - * the given flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_SYNCHRONIZED} flag - */ - public static boolean isSynchronized(int flags) { - return (flags & ACC_SYNCHRONIZED) != 0; + if ((flags & ACC_INTERFACE) != 0) { + sb.append("|interface"); } - - /** - * Returns whether the flag {@code ACC_ABSTRACT} is on in the given - * flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_ABSTRACT} flag - */ - public static boolean isAbstract(int flags) { - return (flags & ACC_ABSTRACT) != 0; + if ((flags & ACC_ABSTRACT) != 0) { + sb.append("|abstract"); } - - /** - * Returns whether the flag {@code ACC_NATIVE} is on in the given - * flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_NATIVE} flag - */ - public static boolean isNative(int flags) { - return (flags & ACC_NATIVE) != 0; + if ((flags & ACC_STRICT) != 0) { + sb.append("|strictfp"); } - - /** - * Returns whether the flag {@code ACC_ANNOTATION} is on in the given - * flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_ANNOTATION} flag - */ - public static boolean isAnnotation(int flags) { - return (flags & ACC_ANNOTATION) != 0; + if ((flags & ACC_SYNTHETIC) != 0) { + sb.append("|synthetic"); } - - /** - * Returns whether the flag {@code ACC_DECLARED_SYNCHRONIZED} is - * on in the given flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_DECLARED_SYNCHRONIZED} flag - */ - public static boolean isDeclaredSynchronized(int flags) { - return (flags & ACC_DECLARED_SYNCHRONIZED) != 0; + if ((flags & ACC_ANNOTATION) != 0) { + sb.append("|annotation"); } - - /** - * Returns whether the flag {@code ACC_ENUM} is on in the given flags. - * - * @param flags the flags to check - * @return the value of the {@code ACC_ENUM} flag - */ - public static boolean isEnum(int flags) { - return (flags & ACC_ENUM) != 0; + if ((flags & ACC_ENUM) != 0) { + sb.append("|enum"); + } + if ((flags & ACC_CONSTRUCTOR) != 0) { + sb.append("|constructor"); + } + if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) { + sb.append("|declared_synchronized"); } - /** - * Helper to return a human-oriented string representing the given - * access flags. - * - * @param flags the defined flags - * @param mask mask for the "defined" bits - * @param what what the flags represent (one of {@code CONV_*}) - * @return {@code non-null;} human-oriented string - */ - private static String humanHelper(int flags, int mask, int what) { - StringBuffer sb = new StringBuffer(80); - int extra = flags & ~mask; - - flags &= mask; - - if ((flags & ACC_PUBLIC) != 0) { - sb.append("|public"); - } - if ((flags & ACC_PRIVATE) != 0) { - sb.append("|private"); - } - if ((flags & ACC_PROTECTED) != 0) { - sb.append("|protected"); - } - if ((flags & ACC_STATIC) != 0) { - sb.append("|static"); - } - if ((flags & ACC_FINAL) != 0) { - sb.append("|final"); - } - if ((flags & ACC_SYNCHRONIZED) != 0) { - if (what == CONV_CLASS) { - sb.append("|super"); - } else { - sb.append("|synchronized"); - } - } - if ((flags & ACC_VOLATILE) != 0) { - if (what == CONV_METHOD) { - sb.append("|bridge"); - } else { - sb.append("|volatile"); - } - } - if ((flags & ACC_TRANSIENT) != 0) { - if (what == CONV_METHOD) { - sb.append("|varargs"); - } else { - sb.append("|transient"); - } - } - if ((flags & ACC_NATIVE) != 0) { - sb.append("|native"); - } - if ((flags & ACC_INTERFACE) != 0) { - sb.append("|interface"); - } - if ((flags & ACC_ABSTRACT) != 0) { - sb.append("|abstract"); - } - if ((flags & ACC_STRICT) != 0) { - sb.append("|strictfp"); - } - if ((flags & ACC_SYNTHETIC) != 0) { - sb.append("|synthetic"); - } - if ((flags & ACC_ANNOTATION) != 0) { - sb.append("|annotation"); - } - if ((flags & ACC_ENUM) != 0) { - sb.append("|enum"); - } - if ((flags & ACC_CONSTRUCTOR) != 0) { - sb.append("|constructor"); - } - if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) { - sb.append("|declared_synchronized"); - } - - if ((extra != 0) || (sb.length() == 0)) { - sb.append('|'); - sb.append(Hex.u2(extra)); - } - - return sb.substring(1); + if ((extra != 0) || (sb.length() == 0)) { + sb.append('|'); + sb.append(Hex.u2(extra)); } + + return sb.substring(1); + } } diff --git a/dx/src/com/android/jack/dx/rop/code/BasicBlock.java b/dx/src/com/android/jack/dx/rop/code/BasicBlock.java index c87fc52..6fa8306 100644 --- a/dx/src/com/android/jack/dx/rop/code/BasicBlock.java +++ b/dx/src/com/android/jack/dx/rop/code/BasicBlock.java @@ -25,257 +25,254 @@ import com.android.jack.dx.util.LabeledItem; * Basic block of register-based instructions. */ public final class BasicBlock implements LabeledItem { - /** {@code >= 0;} target label for this block */ - private final int label; - - /** {@code non-null;} list of instructions in this block */ - private final InsnList insns; - - /** - * {@code non-null;} full list of successors that this block may - * branch to - */ - private final IntList successors; - - /** - * {@code >= -1;} the primary / standard-flow / "default" successor, or - * {@code -1} if this block has no successors (that is, it - * exits the function/method) - */ - private final int primarySuccessor; - - /** - * Constructs an instance. The predecessor set is set to {@code null}. - * - * @param label {@code >= 0;} target label for this block - * @param insns {@code non-null;} list of instructions in this block - * @param successors {@code non-null;} full list of successors that this - * block may branch to - * @param primarySuccessor {@code >= -1;} the primary / standard-flow / - * "default" successor, or {@code -1} if this block has no - * successors (that is, it exits the function/method or is an - * unconditional throw) - */ - public BasicBlock(int label, InsnList insns, IntList successors, - int primarySuccessor) { - if (label < 0) { - throw new IllegalArgumentException("label < 0"); - } - - try { - insns.throwIfMutable(); - } catch (NullPointerException ex) { - // Elucidate exception. - throw new NullPointerException("insns == null"); - } - - int sz = insns.size(); - - if (sz == 0) { - throw new IllegalArgumentException("insns.size() == 0"); - } - - for (int i = sz - 2; i >= 0; i--) { - Rop one = insns.get(i).getOpcode(); - if (one.getBranchingness() != Rop.BRANCH_NONE) { - throw new IllegalArgumentException("insns[" + i + "] is a " + - "branch or can throw"); - } - } - - Insn lastInsn = insns.get(sz - 1); - if (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) { - throw new IllegalArgumentException("insns does not end with " + - "a branch or throwing " + - "instruction"); - } - - try { - successors.throwIfMutable(); - } catch (NullPointerException ex) { - // Elucidate exception. - throw new NullPointerException("successors == null"); - } - - if (primarySuccessor < -1) { - throw new IllegalArgumentException("primarySuccessor < -1"); - } - - if (primarySuccessor >= 0 && !successors.contains(primarySuccessor)) { - throw new IllegalArgumentException( - "primarySuccessor " + primarySuccessor + " not in successors " + successors); - } - - this.label = label; - this.insns = insns; - this.successors = successors; - this.primarySuccessor = primarySuccessor; - } - - /** - * {@inheritDoc} - * - * Instances of this class compare by identity. That is, - * {@code x.equals(y)} is only true if {@code x == y}. - */ - @Override - public boolean equals(Object other) { - return (this == other); - } - - /** - * {@inheritDoc} - * - * Return the identity hashcode of this instance. This is proper, - * since instances of this class compare by identity (see {@link #equals}). - */ - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - /** - * Gets the target label of this block. - * - * @return {@code >= 0;} the label - */ - public int getLabel() { - return label; - } - - /** - * Gets the list of instructions inside this block. - * - * @return {@code non-null;} the instruction list - */ - public InsnList getInsns() { - return insns; + /** {@code >= 0;} target label for this block */ + private final int label; + + /** {@code non-null;} list of instructions in this block */ + private final InsnList insns; + + /** + * {@code non-null;} full list of successors that this block may + * branch to + */ + private final IntList successors; + + /** + * {@code >= -1;} the primary / standard-flow / "default" successor, or + * {@code -1} if this block has no successors (that is, it + * exits the function/method) + */ + private final int primarySuccessor; + + /** + * Constructs an instance. The predecessor set is set to {@code null}. + * + * @param label {@code >= 0;} target label for this block + * @param insns {@code non-null;} list of instructions in this block + * @param successors {@code non-null;} full list of successors that this + * block may branch to + * @param primarySuccessor {@code >= -1;} the primary / standard-flow / + * "default" successor, or {@code -1} if this block has no + * successors (that is, it exits the function/method or is an + * unconditional throw) + */ + public BasicBlock(int label, InsnList insns, IntList successors, int primarySuccessor) { + if (label < 0) { + throw new IllegalArgumentException("label < 0"); } - /** - * Gets the list of successors that this block may branch to. - * - * @return {@code non-null;} the successors list - */ - public IntList getSuccessors() { - return successors; + try { + insns.throwIfMutable(); + } catch (NullPointerException ex) { + // Elucidate exception. + throw new NullPointerException("insns == null"); } - /** - * Gets the primary successor of this block. - * - * @return {@code >= -1;} the primary successor, or {@code -1} if this - * block has no successors at all - */ - public int getPrimarySuccessor() { - return primarySuccessor; - } + int sz = insns.size(); - /** - * Gets the secondary successor of this block. It is only valid to call - * this method on blocks that have exactly two successors. - * - * @return {@code >= 0;} the secondary successor - */ - public int getSecondarySuccessor() { - if (successors.size() != 2) { - throw new UnsupportedOperationException( - "block doesn't have exactly two successors"); - } - - int succ = successors.get(0); - if (succ == primarySuccessor) { - succ = successors.get(1); - } - - return succ; + if (sz == 0) { + throw new IllegalArgumentException("insns.size() == 0"); } - /** - * Gets the first instruction of this block. This is just a - * convenient shorthand for {@code getInsns().get(0)}. - * - * @return {@code non-null;} the first instruction - */ - public Insn getFirstInsn() { - return insns.get(0); + for (int i = sz - 2; i >= 0; i--) { + Rop one = insns.get(i).getOpcode(); + if (one.getBranchingness() != Rop.BRANCH_NONE) { + throw new IllegalArgumentException("insns[" + i + "] is a " + "branch or can throw"); + } } - /** - * Gets the last instruction of this block. This is just a - * convenient shorthand for {@code getInsns().getLast()}. - * - * @return {@code non-null;} the last instruction - */ - public Insn getLastInsn() { - return insns.getLast(); + Insn lastInsn = insns.get(sz - 1); + if (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) { + throw new IllegalArgumentException( + "insns does not end with " + "a branch or throwing " + "instruction"); } - /** - * Returns whether this block might throw an exception. This is - * just a convenient shorthand for {@code getLastInsn().canThrow()}. - * - * @return {@code true} iff this block might throw an - * exception - */ - public boolean canThrow() { - return insns.getLast().canThrow(); + try { + successors.throwIfMutable(); + } catch (NullPointerException ex) { + // Elucidate exception. + throw new NullPointerException("successors == null"); } - /** - * Returns whether this block has any associated exception handlers. - * This is just a shorthand for inspecting the last instruction in - * the block to see if it could throw, and if so, whether it in fact - * has any associated handlers. - * - * @return {@code true} iff this block has any associated - * exception handlers - */ - public boolean hasExceptionHandlers() { - Insn lastInsn = insns.getLast(); - return lastInsn.getCatches().size() != 0; + if (primarySuccessor < -1) { + throw new IllegalArgumentException("primarySuccessor < -1"); } - /** - * Returns the exception handler types associated with this block, - * if any. This is just a shorthand for inspecting the last - * instruction in the block to see if it could throw, and if so, - * grabbing the catch list out of it. If not, this returns an - * empty list (not {@code null}). - * - * @return {@code non-null;} the exception handler types associated with - * this block - */ - public TypeList getExceptionHandlerTypes() { - Insn lastInsn = insns.getLast(); - return lastInsn.getCatches(); + if (primarySuccessor >= 0 && !successors.contains(primarySuccessor)) { + throw new IllegalArgumentException( + "primarySuccessor " + primarySuccessor + " not in successors " + successors); } - /** - * Returns an instance that is identical to this one, except that - * the registers in each instruction are offset by the given - * amount. - * - * @param delta the amount to offset register numbers by - * @return {@code non-null;} an appropriately-constructed instance - */ - public BasicBlock withRegisterOffset(int delta) { - return new BasicBlock(label, insns.withRegisterOffset(delta), - successors, primarySuccessor); + this.label = label; + this.insns = insns; + this.successors = successors; + this.primarySuccessor = primarySuccessor; + } + + /** + * {@inheritDoc} + * + * Instances of this class compare by identity. That is, + * {@code x.equals(y)} is only true if {@code x == y}. + */ + @Override + public boolean equals(Object other) { + return (this == other); + } + + /** + * {@inheritDoc} + * + * Return the identity hashcode of this instance. This is proper, + * since instances of this class compare by identity (see {@link #equals}). + */ + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + /** + * Gets the target label of this block. + * + * @return {@code >= 0;} the label + */ + @Override + public int getLabel() { + return label; + } + + /** + * Gets the list of instructions inside this block. + * + * @return {@code non-null;} the instruction list + */ + public InsnList getInsns() { + return insns; + } + + /** + * Gets the list of successors that this block may branch to. + * + * @return {@code non-null;} the successors list + */ + public IntList getSuccessors() { + return successors; + } + + /** + * Gets the primary successor of this block. + * + * @return {@code >= -1;} the primary successor, or {@code -1} if this + * block has no successors at all + */ + public int getPrimarySuccessor() { + return primarySuccessor; + } + + /** + * Gets the secondary successor of this block. It is only valid to call + * this method on blocks that have exactly two successors. + * + * @return {@code >= 0;} the secondary successor + */ + public int getSecondarySuccessor() { + if (successors.size() != 2) { + throw new UnsupportedOperationException("block doesn't have exactly two successors"); } - public String toString() { - return '{' + Hex.u2(label) + '}'; + int succ = successors.get(0); + if (succ == primarySuccessor) { + succ = successors.get(1); } + return succ; + } + + /** + * Gets the first instruction of this block. This is just a + * convenient shorthand for {@code getInsns().get(0)}. + * + * @return {@code non-null;} the first instruction + */ + public Insn getFirstInsn() { + return insns.get(0); + } + + /** + * Gets the last instruction of this block. This is just a + * convenient shorthand for {@code getInsns().getLast()}. + * + * @return {@code non-null;} the last instruction + */ + public Insn getLastInsn() { + return insns.getLast(); + } + + /** + * Returns whether this block might throw an exception. This is + * just a convenient shorthand for {@code getLastInsn().canThrow()}. + * + * @return {@code true} iff this block might throw an + * exception + */ + public boolean canThrow() { + return insns.getLast().canThrow(); + } + + /** + * Returns whether this block has any associated exception handlers. + * This is just a shorthand for inspecting the last instruction in + * the block to see if it could throw, and if so, whether it in fact + * has any associated handlers. + * + * @return {@code true} iff this block has any associated + * exception handlers + */ + public boolean hasExceptionHandlers() { + Insn lastInsn = insns.getLast(); + return lastInsn.getCatches().size() != 0; + } + + /** + * Returns the exception handler types associated with this block, + * if any. This is just a shorthand for inspecting the last + * instruction in the block to see if it could throw, and if so, + * grabbing the catch list out of it. If not, this returns an + * empty list (not {@code null}). + * + * @return {@code non-null;} the exception handler types associated with + * this block + */ + public TypeList getExceptionHandlerTypes() { + Insn lastInsn = insns.getLast(); + return lastInsn.getCatches(); + } + + /** + * Returns an instance that is identical to this one, except that + * the registers in each instruction are offset by the given + * amount. + * + * @param delta the amount to offset register numbers by + * @return {@code non-null;} an appropriately-constructed instance + */ + public BasicBlock withRegisterOffset(int delta) { + return new BasicBlock(label, insns.withRegisterOffset(delta), successors, primarySuccessor); + } + + @Override + public String toString() { + return '{' + Hex.u2(label) + '}'; + } + + /** + * BasicBlock visitor interface + */ + public interface Visitor { /** - * BasicBlock visitor interface + * Visits a basic block + * @param b block visited */ - public interface Visitor { - /** - * Visits a basic block - * @param b block visited - */ - public void visitBlock (BasicBlock b); - } + public void visitBlock(BasicBlock b); + } } diff --git a/dx/src/com/android/jack/dx/rop/code/BasicBlockList.java b/dx/src/com/android/jack/dx/rop/code/BasicBlockList.java index 3d5ddca..150dbe2 100644 --- a/dx/src/com/android/jack/dx/rop/code/BasicBlockList.java +++ b/dx/src/com/android/jack/dx/rop/code/BasicBlockList.java @@ -26,371 +26,374 @@ import com.android.jack.dx.util.LabeledList; * List of {@link BasicBlock} instances. */ public final class BasicBlockList extends LabeledList { - /** - * {@code >= -1;} the count of registers required by this method or - * {@code -1} if not yet calculated - */ - private int regCount; - - /** - * Constructs an instance. All indices initially contain {@code null}, - * and the first-block label is initially {@code -1}. - * - * @param size the size of the list - */ - public BasicBlockList(int size) { - super(size); + /** + * {@code >= -1;} the count of registers required by this method or + * {@code -1} if not yet calculated + */ + private int regCount; + + /** + * Constructs an instance. All indices initially contain {@code null}, + * and the first-block label is initially {@code -1}. + * + * @param size the size of the list + */ + public BasicBlockList(int size) { + super(size); + + regCount = -1; + } + + /** + * Constructs a mutable copy for {@code getMutableCopy()}. + * + * @param old block to copy + */ + private BasicBlockList(BasicBlockList old) { + super(old); + regCount = old.regCount; + } + + + /** + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index + */ + public BasicBlock get(int n) { + return (BasicBlock) get0(n); + } + + /** + * Sets the basic block at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param bb {@code null-ok;} the element to set at {@code n} + */ + public void set(int n, BasicBlock bb) { + super.set(n, bb); + + // Reset regCount, since it will need to be recalculated. + regCount = -1; + } + + /** + * Returns how many registers this method requires. This is simply + * the maximum of register-number-plus-category referred to by this + * instance's instructions (indirectly through {@link BasicBlock} + * instances). + * + * @return {@code >= 0;} the register count + */ + public int getRegCount() { + if (regCount == -1) { + RegCountVisitor visitor = new RegCountVisitor(); + forEachInsn(visitor); + regCount = visitor.getRegCount(); + } - regCount = -1; + return regCount; + } + + /** + * Gets the total instruction count for this instance. This is the + * sum of the instruction counts of each block. + * + * @return {@code >= 0;} the total instruction count + */ + public int getInstructionCount() { + int sz = size(); + int result = 0; + + for (int i = 0; i < sz; i++) { + BasicBlock one = (BasicBlock) getOrNull0(i); + if (one != null) { + result += one.getInsns().size(); + } } - /** - * Constructs a mutable copy for {@code getMutableCopy()}. - * - * @param old block to copy - */ - private BasicBlockList(BasicBlockList old) { - super(old); - regCount = old.regCount; + return result; + } + + /** + * Gets the total instruction count for this instance, ignoring + * mark-local instructions which are not actually emitted. + * + * @return {@code >= 0;} the total instruction count + */ + public int getEffectiveInstructionCount() { + int sz = size(); + int result = 0; + + for (int i = 0; i < sz; i++) { + BasicBlock one = (BasicBlock) getOrNull0(i); + if (one != null) { + InsnList insns = one.getInsns(); + int insnsSz = insns.size(); + + for (int j = 0; j < insnsSz; j++) { + Insn insn = insns.get(j); + + if (insn.getOpcode().getOpcode() != RegOps.MARK_LOCAL) { + result++; + } + } + } } + return result; + } + + /** + * Gets the first block in the list with the given label, if any. + * + * @param label {@code >= 0;} the label to look for + * @return {@code non-null;} the so-labelled block + * @throws IllegalArgumentException thrown if the label isn't found + */ + public BasicBlock labelToBlock(int label) { + int idx = indexOfLabel(label); + + if (idx < 0) { + throw new IllegalArgumentException("no such label: " + Hex.u2(label)); + } - /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index - */ - public BasicBlock get(int n) { - return (BasicBlock) get0(n); + return get(idx); + } + + /** + * Visits each instruction of each block in the list, in order. + * + * @param visitor {@code non-null;} visitor to use + */ + public void forEachInsn(Insn.Visitor visitor) { + int sz = size(); + + for (int i = 0; i < sz; i++) { + BasicBlock one = get(i); + InsnList insns = one.getInsns(); + insns.forEach(visitor); + } + } + + /** + * Returns an instance that is identical to this one, except that + * the registers in each instruction are offset by the given + * amount. Mutability of the result is inherited from the + * original. + * + * @param delta the amount to offset register numbers by + * @return {@code non-null;} an appropriately-constructed instance + */ + public BasicBlockList withRegisterOffset(int delta) { + int sz = size(); + BasicBlockList result = new BasicBlockList(sz); + + for (int i = 0; i < sz; i++) { + BasicBlock one = (BasicBlock) get0(i); + if (one != null) { + result.set(i, one.withRegisterOffset(delta)); + } } - /** - * Sets the basic block at the given index. - * - * @param n {@code >= 0, < size();} which index - * @param bb {@code null-ok;} the element to set at {@code n} - */ - public void set(int n, BasicBlock bb) { - super.set(n, bb); + if (isImmutable()) { + result.setImmutable(); + } - // Reset regCount, since it will need to be recalculated. - regCount = -1; + return result; + } + + /** + * Returns a mutable copy of this list. + * + * @return {@code non-null;} an appropriately-constructed instance + */ + public BasicBlockList getMutableCopy() { + return new BasicBlockList(this); + } + + /** + * Gets the preferred successor for the given block. If the block + * only has one successor, then that is the preferred successor. + * Otherwise, if the block has a primay successor, then that is + * the preferred successor. If the block has no successors, then + * this returns {@code null}. + * + * @param block {@code non-null;} the block in question + * @return {@code null-ok;} the preferred successor, if any + */ + public BasicBlock preferredSuccessorOf(BasicBlock block) { + int primarySuccessor = block.getPrimarySuccessor(); + IntList successors = block.getSuccessors(); + int succSize = successors.size(); + + switch (succSize) { + case 0: { + return null; + } + case 1: { + return labelToBlock(successors.get(0)); + } } - /** - * Returns how many registers this method requires. This is simply - * the maximum of register-number-plus-category referred to by this - * instance's instructions (indirectly through {@link BasicBlock} - * instances). - * - * @return {@code >= 0;} the register count - */ - public int getRegCount() { - if (regCount == -1) { - RegCountVisitor visitor = new RegCountVisitor(); - forEachInsn(visitor); - regCount = visitor.getRegCount(); - } + if (primarySuccessor != -1) { + return labelToBlock(primarySuccessor); + } else { + return labelToBlock(successors.get(0)); + } + } + + /** + * Compares the catches of two blocks for equality. This includes + * both the catch types and target labels. + * + * @param block1 {@code non-null;} one block to compare + * @param block2 {@code non-null;} the other block to compare + * @return {@code true} if the two blocks' non-primary successors + * are identical + */ + public boolean catchesEqual(BasicBlock block1, BasicBlock block2) { + TypeList catches1 = block1.getExceptionHandlerTypes(); + TypeList catches2 = block2.getExceptionHandlerTypes(); + + if (!StdTypeList.equalContents(catches1, catches2)) { + return false; + } - return regCount; + IntList succ1 = block1.getSuccessors(); + IntList succ2 = block2.getSuccessors(); + int size = succ1.size(); // Both are guaranteed to be the same size. + + int primary1 = block1.getPrimarySuccessor(); + int primary2 = block2.getPrimarySuccessor(); + + if (((primary1 == -1) || (primary2 == -1)) && (primary1 != primary2)) { + /* + * For the current purpose, both blocks in question must + * either both have a primary or both not have a primary to + * be considered equal, and it turns out here that that's not + * the case. + */ + return false; } - /** - * Gets the total instruction count for this instance. This is the - * sum of the instruction counts of each block. - * - * @return {@code >= 0;} the total instruction count - */ - public int getInstructionCount() { - int sz = size(); - int result = 0; - - for (int i = 0; i < sz; i++) { - BasicBlock one = (BasicBlock) getOrNull0(i); - if (one != null) { - result += one.getInsns().size(); - } + for (int i = 0; i < size; i++) { + int label1 = succ1.get(i); + int label2 = succ2.get(i); + + if (label1 == primary1) { + /* + * It should be the case that block2's primary is at the + * same index. If not, we consider the blocks unequal for + * the current purpose. + */ + if (label2 != primary2) { + return false; } + continue; + } - return result; + if (label1 != label2) { + return false; + } } - /** - * Gets the total instruction count for this instance, ignoring - * mark-local instructions which are not actually emitted. - * - * @return {@code >= 0;} the total instruction count - */ - public int getEffectiveInstructionCount() { - int sz = size(); - int result = 0; - - for (int i = 0; i < sz; i++) { - BasicBlock one = (BasicBlock) getOrNull0(i); - if (one != null) { - InsnList insns = one.getInsns(); - int insnsSz = insns.size(); - - for (int j = 0; j < insnsSz; j++) { - Insn insn = insns.get(j); - - if (insn.getOpcode().getOpcode() != RegOps.MARK_LOCAL) { - result++; - } - } - } - } + return true; + } - return result; - } + /** + * Instruction visitor class for counting registers used. + */ + private static class RegCountVisitor implements Insn.Visitor { + /** {@code >= 0;} register count in-progress */ + private int regCount; /** - * Gets the first block in the list with the given label, if any. - * - * @param label {@code >= 0;} the label to look for - * @return {@code non-null;} the so-labelled block - * @throws IllegalArgumentException thrown if the label isn't found + * Constructs an instance. */ - public BasicBlock labelToBlock(int label) { - int idx = indexOfLabel(label); - - if (idx < 0) { - throw new IllegalArgumentException("no such label: " - + Hex.u2(label)); - } - - return get(idx); + public RegCountVisitor() { + regCount = 0; } /** - * Visits each instruction of each block in the list, in order. + * Gets the register count. * - * @param visitor {@code non-null;} visitor to use + * @return {@code >= 0;} the count */ - public void forEachInsn(Insn.Visitor visitor) { - int sz = size(); - - for (int i = 0; i < sz; i++) { - BasicBlock one = get(i); - InsnList insns = one.getInsns(); - insns.forEach(visitor); - } + public int getRegCount() { + return regCount; } - /** - * Returns an instance that is identical to this one, except that - * the registers in each instruction are offset by the given - * amount. Mutability of the result is inherited from the - * original. - * - * @param delta the amount to offset register numbers by - * @return {@code non-null;} an appropriately-constructed instance - */ - public BasicBlockList withRegisterOffset(int delta) { - int sz = size(); - BasicBlockList result = new BasicBlockList(sz); - - for (int i = 0; i < sz; i++) { - BasicBlock one = (BasicBlock) get0(i); - if (one != null) { - result.set(i, one.withRegisterOffset(delta)); - } - } + /** {@inheritDoc} */ + @Override + public void visitPlainInsn(PlainInsn insn) { + visit(insn); + } - if (isImmutable()) { - result.setImmutable(); - } + /** {@inheritDoc} */ + @Override + public void visitPlainCstInsn(PlainCstInsn insn) { + visit(insn); + } - return result; + /** {@inheritDoc} */ + @Override + public void visitSwitchInsn(SwitchInsn insn) { + visit(insn); } - /** - * Returns a mutable copy of this list. - * - * @return {@code non-null;} an appropriately-constructed instance - */ - public BasicBlockList getMutableCopy() { - return new BasicBlockList(this); + /** {@inheritDoc} */ + @Override + public void visitThrowingCstInsn(ThrowingCstInsn insn) { + visit(insn); } - /** - * Gets the preferred successor for the given block. If the block - * only has one successor, then that is the preferred successor. - * Otherwise, if the block has a primay successor, then that is - * the preferred successor. If the block has no successors, then - * this returns {@code null}. - * - * @param block {@code non-null;} the block in question - * @return {@code null-ok;} the preferred successor, if any - */ - public BasicBlock preferredSuccessorOf(BasicBlock block) { - int primarySuccessor = block.getPrimarySuccessor(); - IntList successors = block.getSuccessors(); - int succSize = successors.size(); - - switch (succSize) { - case 0: { - return null; - } - case 1: { - return labelToBlock(successors.get(0)); - } - } + /** {@inheritDoc} */ + @Override + public void visitThrowingInsn(ThrowingInsn insn) { + visit(insn); + } - if (primarySuccessor != -1) { - return labelToBlock(primarySuccessor); - } else { - return labelToBlock(successors.get(0)); - } + /** {@inheritDoc} */ + @Override + public void visitFillArrayDataInsn(FillArrayDataInsn insn) { + visit(insn); } /** - * Compares the catches of two blocks for equality. This includes - * both the catch types and target labels. + * Helper for all the {@code visit*} methods. * - * @param block1 {@code non-null;} one block to compare - * @param block2 {@code non-null;} the other block to compare - * @return {@code true} if the two blocks' non-primary successors - * are identical + * @param insn {@code non-null;} instruction being visited */ - public boolean catchesEqual(BasicBlock block1, BasicBlock block2) { - TypeList catches1 = block1.getExceptionHandlerTypes(); - TypeList catches2 = block2.getExceptionHandlerTypes(); + private void visit(Insn insn) { + RegisterSpec result = insn.getResult(); - if (!StdTypeList.equalContents(catches1, catches2)) { - return false; - } - - IntList succ1 = block1.getSuccessors(); - IntList succ2 = block2.getSuccessors(); - int size = succ1.size(); // Both are guaranteed to be the same size. - - int primary1 = block1.getPrimarySuccessor(); - int primary2 = block2.getPrimarySuccessor(); - - if (((primary1 == -1) || (primary2 == -1)) - && (primary1 != primary2)) { - /* - * For the current purpose, both blocks in question must - * either both have a primary or both not have a primary to - * be considered equal, and it turns out here that that's not - * the case. - */ - return false; - } + if (result != null) { + processReg(result); + } - for (int i = 0; i < size; i++) { - int label1 = succ1.get(i); - int label2 = succ2.get(i); - - if (label1 == primary1) { - /* - * It should be the case that block2's primary is at the - * same index. If not, we consider the blocks unequal for - * the current purpose. - */ - if (label2 != primary2) { - return false; - } - continue; - } - - if (label1 != label2) { - return false; - } - } + RegisterSpecList sources = insn.getSources(); + int sz = sources.size(); - return true; + for (int i = 0; i < sz; i++) { + processReg(sources.get(i)); + } } /** - * Instruction visitor class for counting registers used. + * Processes the given register spec. + * + * @param spec {@code non-null;} the register spec */ - private static class RegCountVisitor - implements Insn.Visitor { - /** {@code >= 0;} register count in-progress */ - private int regCount; - - /** - * Constructs an instance. - */ - public RegCountVisitor() { - regCount = 0; - } + private void processReg(RegisterSpec spec) { + int reg = spec.getNextReg(); - /** - * Gets the register count. - * - * @return {@code >= 0;} the count - */ - public int getRegCount() { - return regCount; - } - - /** {@inheritDoc} */ - public void visitPlainInsn(PlainInsn insn) { - visit(insn); - } - - /** {@inheritDoc} */ - public void visitPlainCstInsn(PlainCstInsn insn) { - visit(insn); - } - - /** {@inheritDoc} */ - public void visitSwitchInsn(SwitchInsn insn) { - visit(insn); - } - - /** {@inheritDoc} */ - public void visitThrowingCstInsn(ThrowingCstInsn insn) { - visit(insn); - } - - /** {@inheritDoc} */ - public void visitThrowingInsn(ThrowingInsn insn) { - visit(insn); - } - - /** {@inheritDoc} */ - public void visitFillArrayDataInsn(FillArrayDataInsn insn) { - visit(insn); - } - - /** - * Helper for all the {@code visit*} methods. - * - * @param insn {@code non-null;} instruction being visited - */ - private void visit(Insn insn) { - RegisterSpec result = insn.getResult(); - - if (result != null) { - processReg(result); - } - - RegisterSpecList sources = insn.getSources(); - int sz = sources.size(); - - for (int i = 0; i < sz; i++) { - processReg(sources.get(i)); - } - } - - /** - * Processes the given register spec. - * - * @param spec {@code non-null;} the register spec - */ - private void processReg(RegisterSpec spec) { - int reg = spec.getNextReg(); - - if (reg > regCount) { - regCount = reg; - } - } + if (reg > regCount) { + regCount = reg; + } } + } } diff --git a/dx/src/com/android/jack/dx/rop/code/ConservativeTranslationAdvice.java b/dx/src/com/android/jack/dx/rop/code/ConservativeTranslationAdvice.java index 6a4ee22..3077f81 100644 --- a/dx/src/com/android/jack/dx/rop/code/ConservativeTranslationAdvice.java +++ b/dx/src/com/android/jack/dx/rop/code/ConservativeTranslationAdvice.java @@ -20,33 +20,32 @@ package com.android.jack.dx.rop.code; * Implementation of {@link TranslationAdvice} which conservatively answers * {@code false} to all methods. */ -public final class ConservativeTranslationAdvice - implements TranslationAdvice { - /** {@code non-null;} standard instance of this class */ - public static final ConservativeTranslationAdvice THE_ONE = - new ConservativeTranslationAdvice(); +public final class ConservativeTranslationAdvice implements TranslationAdvice { + /** {@code non-null;} standard instance of this class */ + public static final ConservativeTranslationAdvice THE_ONE = new ConservativeTranslationAdvice(); - /** - * This class is not publicly instantiable. Use {@link #THE_ONE}. - */ - private ConservativeTranslationAdvice() { - // This space intentionally left blank. - } + /** + * This class is not publicly instantiable. Use {@link #THE_ONE}. + */ + private ConservativeTranslationAdvice() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - public boolean hasConstantOperation(Rop opcode, - RegisterSpec sourceA, RegisterSpec sourceB) { - return false; - } + /** {@inheritDoc} */ + @Override + public boolean hasConstantOperation(Rop opcode, RegisterSpec sourceA, RegisterSpec sourceB) { + return false; + } - /** {@inheritDoc} */ - public boolean requiresSourcesInOrder(Rop opcode, - RegisterSpecList sources) { - return false; - } + /** {@inheritDoc} */ + @Override + public boolean requiresSourcesInOrder(Rop opcode, RegisterSpecList sources) { + return false; + } - /** {@inheritDoc} */ - public int getMaxOptimalRegisterCount() { - return Integer.MAX_VALUE; - } + /** {@inheritDoc} */ + @Override + public int getMaxOptimalRegisterCount() { + return Integer.MAX_VALUE; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/CstInsn.java b/dx/src/com/android/jack/dx/rop/code/CstInsn.java index 4e5a7fe..c639863 100644 --- a/dx/src/com/android/jack/dx/rop/code/CstInsn.java +++ b/dx/src/com/android/jack/dx/rop/code/CstInsn.java @@ -21,54 +21,52 @@ import com.android.jack.dx.rop.cst.Constant; /** * Instruction which contains an explicit reference to a constant. */ -public abstract class CstInsn - extends Insn { - /** {@code non-null;} the constant */ - private final Constant cst; +public abstract class CstInsn extends Insn { + /** {@code non-null;} the constant */ + private final Constant cst; - /** - * Constructs an instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param result {@code null-ok;} spec for the result, if any - * @param sources {@code non-null;} specs for all the sources - * @param cst {@code non-null;} constant - */ - public CstInsn(Rop opcode, SourcePosition position, RegisterSpec result, - RegisterSpecList sources, Constant cst) { - super(opcode, position, result, sources); - - if (cst == null) { - throw new NullPointerException("cst == null"); - } + /** + * Constructs an instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param result {@code null-ok;} spec for the result, if any + * @param sources {@code non-null;} specs for all the sources + * @param cst {@code non-null;} constant + */ + public CstInsn(Rop opcode, SourcePosition position, RegisterSpec result, RegisterSpecList sources, + Constant cst) { + super(opcode, position, result, sources); - this.cst = cst; + if (cst == null) { + throw new NullPointerException("cst == null"); } - /** {@inheritDoc} */ - @Override - public String getInlineString() { - return cst.toHuman(); - } + this.cst = cst; + } - /** - * Gets the constant. - * - * @return {@code non-null;} the constant - */ - public Constant getConstant() { - return cst; - } + /** {@inheritDoc} */ + @Override + public String getInlineString() { + return cst.toHuman(); + } - /** {@inheritDoc} */ - @Override - public boolean contentEquals(Insn b) { - /* - * The cast (CstInsn)b below should always succeed since - * Insn.contentEquals compares classes of this and b. - */ - return super.contentEquals(b) - && cst.equals(((CstInsn)b).getConstant()); - } + /** + * Gets the constant. + * + * @return {@code non-null;} the constant + */ + public Constant getConstant() { + return cst; + } + + /** {@inheritDoc} */ + @Override + public boolean contentEquals(Insn b) { + /* + * The cast (CstInsn)b below should always succeed since + * Insn.contentEquals compares classes of this and b. + */ + return super.contentEquals(b) && cst.equals(((CstInsn) b).getConstant()); + } } diff --git a/dx/src/com/android/jack/dx/rop/code/DexTranslationAdvice.java b/dx/src/com/android/jack/dx/rop/code/DexTranslationAdvice.java index 4654a39..10d647f 100644 --- a/dx/src/com/android/jack/dx/rop/code/DexTranslationAdvice.java +++ b/dx/src/com/android/jack/dx/rop/code/DexTranslationAdvice.java @@ -23,108 +23,105 @@ import com.android.jack.dx.rop.type.Type; * Implementation of {@link TranslationAdvice} which represents what * the dex format will be able to represent. */ -public final class DexTranslationAdvice - implements TranslationAdvice { - /** {@code non-null;} standard instance of this class */ - public static final DexTranslationAdvice THE_ONE = - new DexTranslationAdvice(); - - /** debug advice for disabling invoke-range optimization */ - public static final DexTranslationAdvice NO_SOURCES_IN_ORDER = - new DexTranslationAdvice(true); - - /** - * The minimum source width, in register units, for an invoke - * instruction that requires its sources to be in order and contiguous. - */ - private static final int MIN_INVOKE_IN_ORDER = 6; - - /** when true: always returns false for requiresSourcesInOrder */ - private final boolean disableSourcesInOrder; - - /** - * This class is not publicly instantiable. Use {@link #THE_ONE}. - */ - private DexTranslationAdvice() { - disableSourcesInOrder = false; +public final class DexTranslationAdvice implements TranslationAdvice { + /** {@code non-null;} standard instance of this class */ + public static final DexTranslationAdvice THE_ONE = new DexTranslationAdvice(); + + /** debug advice for disabling invoke-range optimization */ + public static final DexTranslationAdvice NO_SOURCES_IN_ORDER = new DexTranslationAdvice(true); + + /** + * The minimum source width, in register units, for an invoke + * instruction that requires its sources to be in order and contiguous. + */ + private static final int MIN_INVOKE_IN_ORDER = 6; + + /** when true: always returns false for requiresSourcesInOrder */ + private final boolean disableSourcesInOrder; + + /** + * This class is not publicly instantiable. Use {@link #THE_ONE}. + */ + private DexTranslationAdvice() { + disableSourcesInOrder = false; + } + + private DexTranslationAdvice(boolean disableInvokeRange) { + this.disableSourcesInOrder = disableInvokeRange; + } + + /** {@inheritDoc} */ + @Override + public boolean hasConstantOperation(Rop opcode, RegisterSpec sourceA, RegisterSpec sourceB) { + if (sourceA.getType() != Type.INT) { + return false; } - private DexTranslationAdvice(boolean disableInvokeRange) { - this.disableSourcesInOrder = disableInvokeRange; + // Return false if second source isn't a constant + if (!(sourceB.getTypeBearer() instanceof CstInteger)) { + // Except for rsub-int (reverse sub) where first source is constant + if (sourceA.getTypeBearer() instanceof CstInteger && opcode.getOpcode() == RegOps.SUB) { + CstInteger cst = (CstInteger) sourceA.getTypeBearer(); + return cst.fitsIn16Bits(); + } else { + return false; + } } - /** {@inheritDoc} */ - public boolean hasConstantOperation(Rop opcode, - RegisterSpec sourceA, RegisterSpec sourceB) { - if (sourceA.getType() != Type.INT) { - return false; - } - - // Return false if second source isn't a constant - if (! (sourceB.getTypeBearer() instanceof CstInteger)) { - // Except for rsub-int (reverse sub) where first source is constant - if (sourceA.getTypeBearer() instanceof CstInteger && - opcode.getOpcode() == RegOps.SUB) { - CstInteger cst = (CstInteger) sourceA.getTypeBearer(); - return cst.fitsIn16Bits(); - } else { - return false; - } - } - - CstInteger cst = (CstInteger) sourceB.getTypeBearer(); - - switch (opcode.getOpcode()) { - // These have 8 and 16 bit cst representations - case RegOps.REM: - case RegOps.ADD: - case RegOps.MUL: - case RegOps.DIV: - case RegOps.AND: - case RegOps.OR: - case RegOps.XOR: - return cst.fitsIn16Bits(); - // These only have 8 bit cst reps - case RegOps.SHL: - case RegOps.SHR: - case RegOps.USHR: - return cst.fitsIn8Bits(); - // No sub-const insn, so check if equivalent add-const fits - case RegOps.SUB: - CstInteger cst2 = CstInteger.make(-cst.getValue()); - return cst2.fitsIn16Bits(); - default: - return false; - } + CstInteger cst = (CstInteger) sourceB.getTypeBearer(); + + switch (opcode.getOpcode()) { + // These have 8 and 16 bit cst representations + case RegOps.REM: + case RegOps.ADD: + case RegOps.MUL: + case RegOps.DIV: + case RegOps.AND: + case RegOps.OR: + case RegOps.XOR: + return cst.fitsIn16Bits(); + // These only have 8 bit cst reps + case RegOps.SHL: + case RegOps.SHR: + case RegOps.USHR: + return cst.fitsIn8Bits(); + // No sub-const insn, so check if equivalent add-const fits + case RegOps.SUB: + CstInteger cst2 = CstInteger.make(-cst.getValue()); + return cst2.fitsIn16Bits(); + default: + return false; } - - /** {@inheritDoc} */ - public boolean requiresSourcesInOrder(Rop opcode, - RegisterSpecList sources) { - - return !disableSourcesInOrder && opcode.isCallLike() - && totalRopWidth(sources) >= MIN_INVOKE_IN_ORDER; + } + + /** {@inheritDoc} */ + @Override + public boolean requiresSourcesInOrder(Rop opcode, RegisterSpecList sources) { + + return !disableSourcesInOrder && opcode.isCallLike() + && totalRopWidth(sources) >= MIN_INVOKE_IN_ORDER; + } + + /** + * Calculates the total rop width of the list of SSA registers + * + * @param sources {@code non-null;} list of SSA registers + * @return {@code >= 0;} rop-form width in register units + */ + private int totalRopWidth(RegisterSpecList sources) { + int sz = sources.size(); + int total = 0; + + for (int i = 0; i < sz; i++) { + total += sources.get(i).getCategory(); } - /** - * Calculates the total rop width of the list of SSA registers - * - * @param sources {@code non-null;} list of SSA registers - * @return {@code >= 0;} rop-form width in register units - */ - private int totalRopWidth(RegisterSpecList sources) { - int sz = sources.size(); - int total = 0; - - for (int i = 0; i < sz; i++) { - total += sources.get(i).getCategory(); - } - - return total; - } + return total; + } - /** {@inheritDoc} */ - public int getMaxOptimalRegisterCount() { - return 16; - } + /** {@inheritDoc} */ + @Override + public int getMaxOptimalRegisterCount() { + return 16; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/Exceptions.java b/dx/src/com/android/jack/dx/rop/code/Exceptions.java index f91f459..ff2fe73 100644 --- a/dx/src/com/android/jack/dx/rop/code/Exceptions.java +++ b/dx/src/com/android/jack/dx/rop/code/Exceptions.java @@ -23,111 +23,103 @@ import com.android.jack.dx.rop.type.Type; * Common exception types. */ public final class Exceptions { - /** {@code non-null;} the type {@code java.lang.ArithmeticException} */ - public static final Type TYPE_ArithmeticException = - Type.intern("Ljava/lang/ArithmeticException;"); - - /** - * {@code non-null;} the type - * {@code java.lang.ArrayIndexOutOfBoundsException} - */ - public static final Type TYPE_ArrayIndexOutOfBoundsException = - Type.intern("Ljava/lang/ArrayIndexOutOfBoundsException;"); - - /** {@code non-null;} the type {@code java.lang.ArrayStoreException} */ - public static final Type TYPE_ArrayStoreException = - Type.intern("Ljava/lang/ArrayStoreException;"); - - /** {@code non-null;} the type {@code java.lang.ClassCastException} */ - public static final Type TYPE_ClassCastException = - Type.intern("Ljava/lang/ClassCastException;"); - - /** {@code non-null;} the type {@code java.lang.Error} */ - public static final Type TYPE_Error = Type.intern("Ljava/lang/Error;"); - - /** - * {@code non-null;} the type - * {@code java.lang.IllegalMonitorStateException} - */ - public static final Type TYPE_IllegalMonitorStateException = - Type.intern("Ljava/lang/IllegalMonitorStateException;"); - - /** {@code non-null;} the type {@code java.lang.NegativeArraySizeException} */ - public static final Type TYPE_NegativeArraySizeException = - Type.intern("Ljava/lang/NegativeArraySizeException;"); - - /** {@code non-null;} the type {@code java.lang.NullPointerException} */ - public static final Type TYPE_NullPointerException = - Type.intern("Ljava/lang/NullPointerException;"); - - /** {@code non-null;} the list {@code [java.lang.Error]} */ - public static final StdTypeList LIST_Error = StdTypeList.make(TYPE_Error); - - /** - * {@code non-null;} the list {@code[java.lang.Error, - * java.lang.ArithmeticException]} - */ - public static final StdTypeList LIST_Error_ArithmeticException = - StdTypeList.make(TYPE_Error, TYPE_ArithmeticException); - - /** - * {@code non-null;} the list {@code[java.lang.Error, - * java.lang.ClassCastException]} - */ - public static final StdTypeList LIST_Error_ClassCastException = - StdTypeList.make(TYPE_Error, TYPE_ClassCastException); - - /** - * {@code non-null;} the list {@code [java.lang.Error, - * java.lang.NegativeArraySizeException]} - */ - public static final StdTypeList LIST_Error_NegativeArraySizeException = - StdTypeList.make(TYPE_Error, TYPE_NegativeArraySizeException); - - /** - * {@code non-null;} the list {@code [java.lang.Error, - * java.lang.NullPointerException]} - */ - public static final StdTypeList LIST_Error_NullPointerException = - StdTypeList.make(TYPE_Error, TYPE_NullPointerException); - - /** - * {@code non-null;} the list {@code [java.lang.Error, - * java.lang.NullPointerException, - * java.lang.ArrayIndexOutOfBoundsException]} - */ - public static final StdTypeList LIST_Error_Null_ArrayIndexOutOfBounds = - StdTypeList.make(TYPE_Error, - TYPE_NullPointerException, - TYPE_ArrayIndexOutOfBoundsException); - - /** - * {@code non-null;} the list {@code [java.lang.Error, - * java.lang.NullPointerException, - * java.lang.ArrayIndexOutOfBoundsException, - * java.lang.ArrayStoreException]} - */ - public static final StdTypeList LIST_Error_Null_ArrayIndex_ArrayStore = - StdTypeList.make(TYPE_Error, - TYPE_NullPointerException, - TYPE_ArrayIndexOutOfBoundsException, - TYPE_ArrayStoreException); - - /** - * {@code non-null;} the list {@code [java.lang.Error, - * java.lang.NullPointerException, - * java.lang.IllegalMonitorStateException]} - */ - public static final StdTypeList - LIST_Error_Null_IllegalMonitorStateException = - StdTypeList.make(TYPE_Error, - TYPE_NullPointerException, - TYPE_IllegalMonitorStateException); - - /** - * This class is uninstantiable. - */ - private Exceptions() { - // This space intentionally left blank. - } + /** {@code non-null;} the type {@code java.lang.ArithmeticException} */ + public static final Type TYPE_ArithmeticException = + Type.intern("Ljava/lang/ArithmeticException;"); + + /** + * {@code non-null;} the type + * {@code java.lang.ArrayIndexOutOfBoundsException} + */ + public static final Type TYPE_ArrayIndexOutOfBoundsException = + Type.intern("Ljava/lang/ArrayIndexOutOfBoundsException;"); + + /** {@code non-null;} the type {@code java.lang.ArrayStoreException} */ + public static final Type TYPE_ArrayStoreException = + Type.intern("Ljava/lang/ArrayStoreException;"); + + /** {@code non-null;} the type {@code java.lang.ClassCastException} */ + public static final Type TYPE_ClassCastException = Type.intern("Ljava/lang/ClassCastException;"); + + /** {@code non-null;} the type {@code java.lang.Error} */ + public static final Type TYPE_Error = Type.intern("Ljava/lang/Error;"); + + /** + * {@code non-null;} the type + * {@code java.lang.IllegalMonitorStateException} + */ + public static final Type TYPE_IllegalMonitorStateException = + Type.intern("Ljava/lang/IllegalMonitorStateException;"); + + /** {@code non-null;} the type {@code java.lang.NegativeArraySizeException} */ + public static final Type TYPE_NegativeArraySizeException = + Type.intern("Ljava/lang/NegativeArraySizeException;"); + + /** {@code non-null;} the type {@code java.lang.NullPointerException} */ + public static final Type TYPE_NullPointerException = + Type.intern("Ljava/lang/NullPointerException;"); + + /** {@code non-null;} the list {@code [java.lang.Error]} */ + public static final StdTypeList LIST_Error = StdTypeList.make(TYPE_Error); + + /** + * {@code non-null;} the list {@code[java.lang.Error, + * java.lang.ArithmeticException]} + */ + public static final StdTypeList LIST_Error_ArithmeticException = + StdTypeList.make(TYPE_Error, TYPE_ArithmeticException); + + /** + * {@code non-null;} the list {@code[java.lang.Error, + * java.lang.ClassCastException]} + */ + public static final StdTypeList LIST_Error_ClassCastException = + StdTypeList.make(TYPE_Error, TYPE_ClassCastException); + + /** + * {@code non-null;} the list {@code [java.lang.Error, + * java.lang.NegativeArraySizeException]} + */ + public static final StdTypeList LIST_Error_NegativeArraySizeException = + StdTypeList.make(TYPE_Error, TYPE_NegativeArraySizeException); + + /** + * {@code non-null;} the list {@code [java.lang.Error, + * java.lang.NullPointerException]} + */ + public static final StdTypeList LIST_Error_NullPointerException = + StdTypeList.make(TYPE_Error, TYPE_NullPointerException); + + /** + * {@code non-null;} the list {@code [java.lang.Error, + * java.lang.NullPointerException, + * java.lang.ArrayIndexOutOfBoundsException]} + */ + public static final StdTypeList LIST_Error_Null_ArrayIndexOutOfBounds = + StdTypeList.make(TYPE_Error, TYPE_NullPointerException, TYPE_ArrayIndexOutOfBoundsException); + + /** + * {@code non-null;} the list {@code [java.lang.Error, + * java.lang.NullPointerException, + * java.lang.ArrayIndexOutOfBoundsException, + * java.lang.ArrayStoreException]} + */ + public static final StdTypeList LIST_Error_Null_ArrayIndex_ArrayStore = StdTypeList.make( + TYPE_Error, TYPE_NullPointerException, TYPE_ArrayIndexOutOfBoundsException, + TYPE_ArrayStoreException); + + /** + * {@code non-null;} the list {@code [java.lang.Error, + * java.lang.NullPointerException, + * java.lang.IllegalMonitorStateException]} + */ + public static final StdTypeList LIST_Error_Null_IllegalMonitorStateException = + StdTypeList.make(TYPE_Error, TYPE_NullPointerException, TYPE_IllegalMonitorStateException); + + /** + * This class is uninstantiable. + */ + private Exceptions() { + // This space intentionally left blank. + } } diff --git a/dx/src/com/android/jack/dx/rop/code/FillArrayDataInsn.java b/dx/src/com/android/jack/dx/rop/code/FillArrayDataInsn.java index f92fd71..fb0ebb3 100644 --- a/dx/src/com/android/jack/dx/rop/code/FillArrayDataInsn.java +++ b/dx/src/com/android/jack/dx/rop/code/FillArrayDataInsn.java @@ -27,90 +27,84 @@ import java.util.ArrayList; * Instruction which fills a newly created array with a predefined list of * constant values. */ -public final class FillArrayDataInsn - extends Insn { - - /** non-null: initial values to fill the newly created array */ - private final ArrayList<Constant> initValues; - - /** - * non-null: type of the array. Will be used to determine the width of - * elements in the array-data table. - */ - private final Constant arrayType; - - /** - * Constructs an instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param sources {@code non-null;} specs for all the sources - * @param initValues {@code non-null;} list of initial values to fill the array - * @param cst {@code non-null;} type of the new array - */ - public FillArrayDataInsn(Rop opcode, SourcePosition position, - RegisterSpecList sources, - ArrayList<Constant> initValues, - Constant cst) { - super(opcode, position, null, sources); - - if (opcode.getBranchingness() != Rop.BRANCH_NONE) { - throw new IllegalArgumentException("bogus branchingness"); - } - - this.initValues = initValues; - this.arrayType = cst; +public final class FillArrayDataInsn extends Insn { + + /** non-null: initial values to fill the newly created array */ + private final ArrayList<Constant> initValues; + + /** + * non-null: type of the array. Will be used to determine the width of + * elements in the array-data table. + */ + private final Constant arrayType; + + /** + * Constructs an instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param sources {@code non-null;} specs for all the sources + * @param initValues {@code non-null;} list of initial values to fill the array + * @param cst {@code non-null;} type of the new array + */ + public FillArrayDataInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, + ArrayList<Constant> initValues, Constant cst) { + super(opcode, position, null, sources); + + if (opcode.getBranchingness() != Rop.BRANCH_NONE) { + throw new IllegalArgumentException("bogus branchingness"); } - - /** {@inheritDoc} */ - @Override - public TypeList getCatches() { - return StdTypeList.EMPTY; - } - - /** - * Return the list of init values - * @return {@code non-null;} list of init values - */ - public ArrayList<Constant> getInitValues() { - return initValues; - } - - /** - * Return the type of the newly created array - * @return {@code non-null;} array type - */ - public Constant getConstant() { - return arrayType; - } - - /** {@inheritDoc} */ - @Override - public void accept(Visitor visitor) { - visitor.visitFillArrayDataInsn(this); - } - - /** {@inheritDoc} */ - @Override - public Insn withAddedCatch(Type type) { - throw new UnsupportedOperationException("unsupported"); - } - - /** {@inheritDoc} */ - @Override - public Insn withRegisterOffset(int delta) { - return new FillArrayDataInsn(getOpcode(), getPosition(), - getSources().withOffset(delta), - initValues, arrayType); - } - - /** {@inheritDoc} */ - @Override - public Insn withNewRegisters(RegisterSpec result, - RegisterSpecList sources) { - - return new FillArrayDataInsn(getOpcode(), getPosition(), - sources, initValues, arrayType); - } + this.initValues = initValues; + this.arrayType = cst; + } + + + /** {@inheritDoc} */ + @Override + public TypeList getCatches() { + return StdTypeList.EMPTY; + } + + /** + * Return the list of init values + * @return {@code non-null;} list of init values + */ + public ArrayList<Constant> getInitValues() { + return initValues; + } + + /** + * Return the type of the newly created array + * @return {@code non-null;} array type + */ + public Constant getConstant() { + return arrayType; + } + + /** {@inheritDoc} */ + @Override + public void accept(Visitor visitor) { + visitor.visitFillArrayDataInsn(this); + } + + /** {@inheritDoc} */ + @Override + public Insn withAddedCatch(Type type) { + throw new UnsupportedOperationException("unsupported"); + } + + /** {@inheritDoc} */ + @Override + public Insn withRegisterOffset(int delta) { + return new FillArrayDataInsn(getOpcode(), getPosition(), getSources().withOffset(delta), + initValues, arrayType); + } + + /** {@inheritDoc} */ + @Override + public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) { + + return new FillArrayDataInsn(getOpcode(), getPosition(), sources, initValues, arrayType); + } } diff --git a/dx/src/com/android/jack/dx/rop/code/Insn.java b/dx/src/com/android/jack/dx/rop/code/Insn.java index 312f051..11475a6 100644 --- a/dx/src/com/android/jack/dx/rop/code/Insn.java +++ b/dx/src/com/android/jack/dx/rop/code/Insn.java @@ -28,429 +28,432 @@ import com.android.jack.dx.util.ToHuman; * information. */ public abstract class Insn implements ToHuman { - /** {@code non-null;} opcode */ - private final Rop opcode; - - /** {@code non-null;} source position */ - private final SourcePosition position; - - /** {@code null-ok;} spec for the result of this instruction, if any */ - private final RegisterSpec result; - - /** {@code non-null;} specs for all the sources of this instruction */ - private final RegisterSpecList sources; - - /** - * Constructs an instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param result {@code null-ok;} spec for the result, if any - * @param sources {@code non-null;} specs for all the sources - */ - public Insn(Rop opcode, SourcePosition position, RegisterSpec result, - RegisterSpecList sources) { - if (opcode == null) { - throw new NullPointerException("opcode == null"); - } - - if (position == null) { - throw new NullPointerException("position == null"); - } - - if (sources == null) { - throw new NullPointerException("sources == null"); - } - - this.opcode = opcode; - this.position = position; - this.result = result; - this.sources = sources; + /** {@code non-null;} opcode */ + private final Rop opcode; + + /** {@code non-null;} source position */ + private final SourcePosition position; + + /** {@code null-ok;} spec for the result of this instruction, if any */ + private final RegisterSpec result; + + /** {@code non-null;} specs for all the sources of this instruction */ + private final RegisterSpecList sources; + + /** + * Constructs an instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param result {@code null-ok;} spec for the result, if any + * @param sources {@code non-null;} specs for all the sources + */ + public Insn(Rop opcode, SourcePosition position, RegisterSpec result, RegisterSpecList sources) { + if (opcode == null) { + throw new NullPointerException("opcode == null"); } - /** - * {@inheritDoc} - * - * Instances of this class compare by identity. That is, - * {@code x.equals(y)} is only true if {@code x == y}. - */ - @Override - public final boolean equals(Object other) { - return (this == other); + if (position == null) { + throw new NullPointerException("position == null"); } - /** - * {@inheritDoc} - * - * This implementation returns the identity hashcode of this - * instance. This is proper, since instances of this class compare - * by identity (see {@link #equals}). - */ - @Override - public final int hashCode() { - return System.identityHashCode(this); + if (sources == null) { + throw new NullPointerException("sources == null"); } - /** {@inheritDoc} */ - @Override - public String toString() { - return toStringWithInline(getInlineString()); + this.opcode = opcode; + this.position = position; + this.result = result; + this.sources = sources; + } + + /** + * {@inheritDoc} + * + * Instances of this class compare by identity. That is, + * {@code x.equals(y)} is only true if {@code x == y}. + */ + @Override + public final boolean equals(Object other) { + return (this == other); + } + + /** + * {@inheritDoc} + * + * This implementation returns the identity hashcode of this + * instance. This is proper, since instances of this class compare + * by identity (see {@link #equals}). + */ + @Override + public final int hashCode() { + return System.identityHashCode(this); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return toStringWithInline(getInlineString()); + } + + /** + * Gets a human-oriented (and slightly lossy) string for this instance. + * + * @return {@code non-null;} the human string form + */ + @Override + public String toHuman() { + return toHumanWithInline(getInlineString()); + } + + /** + * Gets an "inline" string portion for toHuman(), if available. This + * is the portion that appears after the Rop opcode + * + * @return {@code null-ok;} if non-null, the inline text for toHuman() + */ + public String getInlineString() { + return null; + } + + /** + * Gets the opcode. + * + * @return {@code non-null;} the opcode + */ + public final Rop getOpcode() { + return opcode; + } + + /** + * Gets the source position. + * + * @return {@code non-null;} the source position + */ + public final SourcePosition getPosition() { + return position; + } + + /** + * Gets the result spec, if any. A return value of {@code null} + * means this instruction returns nothing. + * + * @return {@code null-ok;} the result spec, if any + */ + public final RegisterSpec getResult() { + return result; + } + + /** + * Gets the spec of a local variable assignment that occurs at this + * instruction, or null if no local variable assignment occurs. This + * may be the result register, or for {@code mark-local} insns + * it may be the source. + * + * @return {@code null-ok;} a named register spec or null + */ + public final RegisterSpec getLocalAssignment() { + RegisterSpec assignment; + if (opcode.getOpcode() == RegOps.MARK_LOCAL) { + assignment = sources.get(0); + } else { + assignment = result; } - /** - * Gets a human-oriented (and slightly lossy) string for this instance. - * - * @return {@code non-null;} the human string form - */ - public String toHuman() { - return toHumanWithInline(getInlineString()); - } - - /** - * Gets an "inline" string portion for toHuman(), if available. This - * is the portion that appears after the Rop opcode - * - * @return {@code null-ok;} if non-null, the inline text for toHuman() - */ - public String getInlineString() { - return null; + if (assignment == null) { + return null; } - /** - * Gets the opcode. - * - * @return {@code non-null;} the opcode - */ - public final Rop getOpcode() { - return opcode; - } + LocalItem localItem = assignment.getLocalItem(); - /** - * Gets the source position. - * - * @return {@code non-null;} the source position - */ - public final SourcePosition getPosition() { - return position; + if (localItem == null) { + return null; } - /** - * Gets the result spec, if any. A return value of {@code null} - * means this instruction returns nothing. - * - * @return {@code null-ok;} the result spec, if any - */ - public final RegisterSpec getResult() { - return result; + return assignment; + } + + /** + * Gets the source specs. + * + * @return {@code non-null;} the source specs + */ + public final RegisterSpecList getSources() { + return sources; + } + + /** + * Gets whether this instruction can possibly throw an exception. This + * is just a convenient wrapper for {@code getOpcode().canThrow()}. + * + * @return {@code true} iff this instruction can possibly throw + */ + public final boolean canThrow() { + return opcode.canThrow(); + } + + /** + * Gets the list of possibly-caught exceptions. This returns {@link + * StdTypeList#EMPTY} if this instruction has no handlers, + * which can be <i>either</i> if this instruction can't possibly + * throw or if it merely doesn't handle any of its possible + * exceptions. To determine whether this instruction can throw, + * use {@link #canThrow}. + * + * @return {@code non-null;} the catches list + */ + public abstract TypeList getCatches(); + + /** + * Calls the appropriate method on the given visitor, depending on the + * class of this instance. Subclasses must override this. + * + * @param visitor {@code non-null;} the visitor to call on + */ + public abstract void accept(Visitor visitor); + + /** + * Returns an instance that is just like this one, except that it + * has a catch list with the given item appended to the end. This + * method throws an exception if this instance can't possibly + * throw. To determine whether this instruction can throw, use + * {@link #canThrow}. + * + * @param type {@code non-null;} type to append to the catch list + * @return {@code non-null;} an appropriately-constructed instance + */ + public abstract Insn withAddedCatch(Type type); + + /** + * Returns an instance that is just like this one, except that all + * register references have been offset by the given delta. + * + * @param delta the amount to offset register references by + * @return {@code non-null;} an appropriately-constructed instance + */ + public abstract Insn withRegisterOffset(int delta); + + /** + * Returns an instance that is just like this one, except that, if + * possible, the insn is converted into a version in which a source + * (if it is a constant) is represented directly rather than as a + * register reference. {@code this} is returned in cases where the + * translation is not possible. + * + * @return {@code non-null;} an appropriately-constructed instance + */ + public Insn withSourceLiteral() { + return this; + } + + /** + * Returns an exact copy of this Insn + * + * @return {@code non-null;} an appropriately-constructed instance + */ + public Insn copy() { + return withRegisterOffset(0); + } + + + /** + * Compares, handling nulls safely + * + * @param a first object + * @param b second object + * @return true if they're equal or both null. + */ + private static boolean equalsHandleNulls(Object a, Object b) { + return (a == b) || ((a != null) && a.equals(b)); + } + + /** + * Compares Insn contents, since {@code Insn.equals()} is defined + * to be an identity compare. Insn's are {@code contentEquals()} + * if they have the same opcode, registers, source position, and other + * metadata. + * + * @return true in the case described above + */ + public boolean contentEquals(Insn b) { + return opcode == b.getOpcode() && position.equals(b.getPosition()) + && (getClass() == b.getClass()) && equalsHandleNulls(result, b.getResult()) + && equalsHandleNulls(sources, b.getSources()) + && StdTypeList.equalContents(getCatches(), b.getCatches()); + } + + /** + * Returns an instance that is just like this one, except + * with new result and source registers. + * + * @param result {@code null-ok;} new result register + * @param sources {@code non-null;} new sources registers + * @return {@code non-null;} an appropriately-constructed instance + */ + public abstract Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources); + + /** + * Returns the string form of this instance, with the given bit added in + * the standard location for an inline argument. + * + * @param extra {@code null-ok;} the inline argument string + * @return {@code non-null;} the string form + */ + protected final String toStringWithInline(String extra) { + StringBuffer sb = new StringBuffer(80); + + sb.append("Insn{"); + sb.append(position); + sb.append(' '); + sb.append(opcode); + + if (extra != null) { + sb.append(' '); + sb.append(extra); } - /** - * Gets the spec of a local variable assignment that occurs at this - * instruction, or null if no local variable assignment occurs. This - * may be the result register, or for {@code mark-local} insns - * it may be the source. - * - * @return {@code null-ok;} a named register spec or null - */ - public final RegisterSpec getLocalAssignment() { - RegisterSpec assignment; - if (opcode.getOpcode() == RegOps.MARK_LOCAL) { - assignment = sources.get(0); - } else { - assignment = result; - } - - if (assignment == null) { - return null; - } - - LocalItem localItem = assignment.getLocalItem(); - - if (localItem == null) { - return null; - } + sb.append(" :: "); - return assignment; + if (result != null) { + sb.append(result); + sb.append(" <- "); } - /** - * Gets the source specs. - * - * @return {@code non-null;} the source specs - */ - public final RegisterSpecList getSources() { - return sources; + sb.append(sources); + sb.append('}'); + + return sb.toString(); + } + + /** + * Returns the human string form of this instance, with the given + * bit added in the standard location for an inline argument. + * + * @param extra {@code null-ok;} the inline argument string + * @return {@code non-null;} the human string form + */ + protected final String toHumanWithInline(String extra) { + StringBuffer sb = new StringBuffer(80); + + sb.append(position); + sb.append(": "); + sb.append(opcode.getNickname()); + + if (extra != null) { + sb.append("("); + sb.append(extra); + sb.append(")"); } - /** - * Gets whether this instruction can possibly throw an exception. This - * is just a convenient wrapper for {@code getOpcode().canThrow()}. - * - * @return {@code true} iff this instruction can possibly throw - */ - public final boolean canThrow() { - return opcode.canThrow(); + if (result == null) { + sb.append(" ."); + } else { + sb.append(" "); + sb.append(result.toHuman()); } - /** - * Gets the list of possibly-caught exceptions. This returns {@link - * StdTypeList#EMPTY} if this instruction has no handlers, - * which can be <i>either</i> if this instruction can't possibly - * throw or if it merely doesn't handle any of its possible - * exceptions. To determine whether this instruction can throw, - * use {@link #canThrow}. - * - * @return {@code non-null;} the catches list - */ - public abstract TypeList getCatches(); + sb.append(" <-"); - /** - * Calls the appropriate method on the given visitor, depending on the - * class of this instance. Subclasses must override this. - * - * @param visitor {@code non-null;} the visitor to call on - */ - public abstract void accept(Visitor visitor); + int sz = sources.size(); + if (sz == 0) { + sb.append(" ."); + } else { + for (int i = 0; i < sz; i++) { + sb.append(" "); + sb.append(sources.get(i).toHuman()); + } + } - /** - * Returns an instance that is just like this one, except that it - * has a catch list with the given item appended to the end. This - * method throws an exception if this instance can't possibly - * throw. To determine whether this instruction can throw, use - * {@link #canThrow}. - * - * @param type {@code non-null;} type to append to the catch list - * @return {@code non-null;} an appropriately-constructed instance - */ - public abstract Insn withAddedCatch(Type type); + return sb.toString(); + } - /** - * Returns an instance that is just like this one, except that all - * register references have been offset by the given delta. - * - * @param delta the amount to offset register references by - * @return {@code non-null;} an appropriately-constructed instance - */ - public abstract Insn withRegisterOffset(int delta); + /** + * Visitor interface for this (outer) class. + */ + public static interface Visitor { /** - * Returns an instance that is just like this one, except that, if - * possible, the insn is converted into a version in which a source - * (if it is a constant) is represented directly rather than as a - * register reference. {@code this} is returned in cases where the - * translation is not possible. + * Visits a {@link PlainInsn}. * - * @return {@code non-null;} an appropriately-constructed instance + * @param insn {@code non-null;} the instruction to visit */ - public Insn withSourceLiteral() { - return this; - } + public void visitPlainInsn(PlainInsn insn); /** - * Returns an exact copy of this Insn + * Visits a {@link PlainCstInsn}. * - * @return {@code non-null;} an appropriately-constructed instance + * @param insn {@code non-null;} the instruction to visit */ - public Insn copy() { - return withRegisterOffset(0); - } - + public void visitPlainCstInsn(PlainCstInsn insn); /** - * Compares, handling nulls safely + * Visits a {@link SwitchInsn}. * - * @param a first object - * @param b second object - * @return true if they're equal or both null. + * @param insn {@code non-null;} the instruction to visit */ - private static boolean equalsHandleNulls (Object a, Object b) { - return (a == b) || ((a != null) && a.equals(b)); - } + public void visitSwitchInsn(SwitchInsn insn); /** - * Compares Insn contents, since {@code Insn.equals()} is defined - * to be an identity compare. Insn's are {@code contentEquals()} - * if they have the same opcode, registers, source position, and other - * metadata. + * Visits a {@link ThrowingCstInsn}. * - * @return true in the case described above + * @param insn {@code non-null;} the instruction to visit */ - public boolean contentEquals(Insn b) { - return opcode == b.getOpcode() - && position.equals(b.getPosition()) - && (getClass() == b.getClass()) - && equalsHandleNulls(result, b.getResult()) - && equalsHandleNulls(sources, b.getSources()) - && StdTypeList.equalContents(getCatches(), b.getCatches()); - } + public void visitThrowingCstInsn(ThrowingCstInsn insn); /** - * Returns an instance that is just like this one, except - * with new result and source registers. + * Visits a {@link ThrowingInsn}. * - * @param result {@code null-ok;} new result register - * @param sources {@code non-null;} new sources registers - * @return {@code non-null;} an appropriately-constructed instance + * @param insn {@code non-null;} the instruction to visit */ - public abstract Insn withNewRegisters(RegisterSpec result, - RegisterSpecList sources); + public void visitThrowingInsn(ThrowingInsn insn); /** - * Returns the string form of this instance, with the given bit added in - * the standard location for an inline argument. + * Visits a {@link FillArrayDataInsn}. * - * @param extra {@code null-ok;} the inline argument string - * @return {@code non-null;} the string form + * @param insn {@code non-null;} the instruction to visit */ - protected final String toStringWithInline(String extra) { - StringBuffer sb = new StringBuffer(80); - - sb.append("Insn{"); - sb.append(position); - sb.append(' '); - sb.append(opcode); - - if (extra != null) { - sb.append(' '); - sb.append(extra); - } - - sb.append(" :: "); - - if (result != null) { - sb.append(result); - sb.append(" <- "); - } - - sb.append(sources); - sb.append('}'); + public void visitFillArrayDataInsn(FillArrayDataInsn insn); + } + + /** + * Base implementation of {@link Visitor}, which has empty method + * bodies for all methods. + */ + public static class BaseVisitor implements Visitor { + /** {@inheritDoc} */ + @Override + public void visitPlainInsn(PlainInsn insn) { + // This space intentionally left blank. + } - return sb.toString(); + /** {@inheritDoc} */ + @Override + public void visitPlainCstInsn(PlainCstInsn insn) { + // This space intentionally left blank. } - /** - * Returns the human string form of this instance, with the given - * bit added in the standard location for an inline argument. - * - * @param extra {@code null-ok;} the inline argument string - * @return {@code non-null;} the human string form - */ - protected final String toHumanWithInline(String extra) { - StringBuffer sb = new StringBuffer(80); - - sb.append(position); - sb.append(": "); - sb.append(opcode.getNickname()); - - if (extra != null) { - sb.append("("); - sb.append(extra); - sb.append(")"); - } - - if (result == null) { - sb.append(" ."); - } else { - sb.append(" "); - sb.append(result.toHuman()); - } - - sb.append(" <-"); - - int sz = sources.size(); - if (sz == 0) { - sb.append(" ."); - } else { - for (int i = 0; i < sz; i++) { - sb.append(" "); - sb.append(sources.get(i).toHuman()); - } - } - - return sb.toString(); + /** {@inheritDoc} */ + @Override + public void visitSwitchInsn(SwitchInsn insn) { + // This space intentionally left blank. } + /** {@inheritDoc} */ + @Override + public void visitThrowingCstInsn(ThrowingCstInsn insn) { + // This space intentionally left blank. + } - /** - * Visitor interface for this (outer) class. - */ - public static interface Visitor { - /** - * Visits a {@link PlainInsn}. - * - * @param insn {@code non-null;} the instruction to visit - */ - public void visitPlainInsn(PlainInsn insn); - - /** - * Visits a {@link PlainCstInsn}. - * - * @param insn {@code non-null;} the instruction to visit - */ - public void visitPlainCstInsn(PlainCstInsn insn); - - /** - * Visits a {@link SwitchInsn}. - * - * @param insn {@code non-null;} the instruction to visit - */ - public void visitSwitchInsn(SwitchInsn insn); - - /** - * Visits a {@link ThrowingCstInsn}. - * - * @param insn {@code non-null;} the instruction to visit - */ - public void visitThrowingCstInsn(ThrowingCstInsn insn); - - /** - * Visits a {@link ThrowingInsn}. - * - * @param insn {@code non-null;} the instruction to visit - */ - public void visitThrowingInsn(ThrowingInsn insn); - - /** - * Visits a {@link FillArrayDataInsn}. - * - * @param insn {@code non-null;} the instruction to visit - */ - public void visitFillArrayDataInsn(FillArrayDataInsn insn); + /** {@inheritDoc} */ + @Override + public void visitThrowingInsn(ThrowingInsn insn) { + // This space intentionally left blank. } - /** - * Base implementation of {@link Visitor}, which has empty method - * bodies for all methods. - */ - public static class BaseVisitor implements Visitor { - /** {@inheritDoc} */ - public void visitPlainInsn(PlainInsn insn) { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - public void visitPlainCstInsn(PlainCstInsn insn) { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - public void visitSwitchInsn(SwitchInsn insn) { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - public void visitThrowingCstInsn(ThrowingCstInsn insn) { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - public void visitThrowingInsn(ThrowingInsn insn) { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - public void visitFillArrayDataInsn(FillArrayDataInsn insn) { - // This space intentionally left blank. - } + /** {@inheritDoc} */ + @Override + public void visitFillArrayDataInsn(FillArrayDataInsn insn) { + // This space intentionally left blank. } + } } diff --git a/dx/src/com/android/jack/dx/rop/code/InsnList.java b/dx/src/com/android/jack/dx/rop/code/InsnList.java index fb78741..0d55c67 100644 --- a/dx/src/com/android/jack/dx/rop/code/InsnList.java +++ b/dx/src/com/android/jack/dx/rop/code/InsnList.java @@ -21,110 +21,113 @@ import com.android.jack.dx.util.FixedSizeList; /** * List of {@link Insn} instances. */ -public final class InsnList - extends FixedSizeList { - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size the size of the list - */ - public InsnList(int size) { - super(size); +public final class InsnList extends FixedSizeList { + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size the size of the list + */ + public InsnList(int size) { + super(size); + } + + /** + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index + */ + public Insn get(int n) { + return (Insn) get0(n); + } + + /** + * Sets the instruction at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param insn {@code non-null;} the instruction to set at {@code n} + */ + public void set(int n, Insn insn) { + set0(n, insn); + } + + /** + * Gets the last instruction. This is just a convenient shorthand for + * {@code get(size() - 1)}. + * + * @return {@code non-null;} the last instruction + */ + public Insn getLast() { + return get(size() - 1); + } + + /** + * Visits each instruction in the list, in order. + * + * @param visitor {@code non-null;} visitor to use + */ + public void forEach(Insn.Visitor visitor) { + int sz = size(); + + for (int i = 0; i < sz; i++) { + get(i).accept(visitor); } - - /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index - */ - public Insn get(int n) { - return (Insn) get0(n); + } + + /** + * Compares the contents of this {@code InsnList} with another. + * The blocks must have the same number of insns, and each Insn must + * also return true to {@code Insn.contentEquals()}. + * + * @param b to compare + * @return true in the case described above. + */ + public boolean contentEquals(InsnList b) { + if (b == null) { + return false; } - /** - * Sets the instruction at the given index. - * - * @param n {@code >= 0, < size();} which index - * @param insn {@code non-null;} the instruction to set at {@code n} - */ - public void set(int n, Insn insn) { - set0(n, insn); - } + int sz = size(); - /** - * Gets the last instruction. This is just a convenient shorthand for - * {@code get(size() - 1)}. - * - * @return {@code non-null;} the last instruction - */ - public Insn getLast() { - return get(size() - 1); + if (sz != b.size()) { + return false; } - /** - * Visits each instruction in the list, in order. - * - * @param visitor {@code non-null;} visitor to use - */ - public void forEach(Insn.Visitor visitor) { - int sz = size(); - - for (int i = 0; i < sz; i++) { - get(i).accept(visitor); - } + for (int i = 0; i < sz; i++) { + if (!get(i).contentEquals(b.get(i))) { + return false; + } } - /** - * Compares the contents of this {@code InsnList} with another. - * The blocks must have the same number of insns, and each Insn must - * also return true to {@code Insn.contentEquals()}. - * - * @param b to compare - * @return true in the case described above. - */ - public boolean contentEquals(InsnList b) { - if (b == null) return false; - - int sz = size(); - - if (sz != b.size()) return false; - - for (int i = 0; i < sz; i++) { - if (!get(i).contentEquals(b.get(i))) { - return false; - } - } - - return true; + return true; + } + + /** + * Returns an instance that is identical to this one, except that + * the registers in each instruction are offset by the given + * amount. Mutability of the result is inherited from the + * original. + * + * @param delta the amount to offset register numbers by + * @return {@code non-null;} an appropriately-constructed instance + */ + public InsnList withRegisterOffset(int delta) { + int sz = size(); + InsnList result = new InsnList(sz); + + for (int i = 0; i < sz; i++) { + Insn one = (Insn) get0(i); + if (one != null) { + result.set0(i, one.withRegisterOffset(delta)); + } } - /** - * Returns an instance that is identical to this one, except that - * the registers in each instruction are offset by the given - * amount. Mutability of the result is inherited from the - * original. - * - * @param delta the amount to offset register numbers by - * @return {@code non-null;} an appropriately-constructed instance - */ - public InsnList withRegisterOffset(int delta) { - int sz = size(); - InsnList result = new InsnList(sz); - - for (int i = 0; i < sz; i++) { - Insn one = (Insn) get0(i); - if (one != null) { - result.set0(i, one.withRegisterOffset(delta)); - } - } - - if (isImmutable()) { - result.setImmutable(); - } - - return result; + if (isImmutable()) { + result.setImmutable(); } + + return result; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/LocalItem.java b/dx/src/com/android/jack/dx/rop/code/LocalItem.java index fd9c952..9ebf093 100644 --- a/dx/src/com/android/jack/dx/rop/code/LocalItem.java +++ b/dx/src/com/android/jack/dx/rop/code/LocalItem.java @@ -23,35 +23,35 @@ import com.android.jack.dx.rop.cst.CstType; * A local variable item: either a name or a signature or both. */ public class LocalItem implements Comparable<LocalItem> { - /** {@code null-ok;} local variable name */ - private final CstString name; + /** {@code null-ok;} local variable name */ + private final CstString name; - /** {@code null-ok;} local variable type */ - private final CstType type; + /** {@code null-ok;} local variable type */ + private final CstType type; - /** {@code null-ok;} local variable signature */ - private final CstString signature; + /** {@code null-ok;} local variable signature */ + private final CstString signature; - /** - * Make a new item. If both name and type are null, null is returned. - * - * TODO: intern these - * - * @param name {@code null-ok;} local variable name - * @param type {@code null-ok;} local variable type - * @param signature {@code null-ok;} local variable signature which will be referenced as-is by - * the sig_idx in the debug_info_item (cf. the documentation for debug_info_item and the - * discussion under {@code dalvik.annotation.Signature} in "dex-format.html") - * @return {@code null-ok;} appropriate instance. - */ - public static LocalItem make(CstString name, CstType type, CstString signature) { - if (name == null && type == null) { - return null; - } - - return new LocalItem (name, type, signature); + /** + * Make a new item. If both name and type are null, null is returned. + * + * TODO(dx team): intern these + * + * @param name {@code null-ok;} local variable name + * @param type {@code null-ok;} local variable type + * @param signature {@code null-ok;} local variable signature which will be referenced as-is by + * the sig_idx in the debug_info_item (cf. the documentation for debug_info_item and the + * discussion under {@code dalvik.annotation.Signature} in "dex-format.html") + * @return {@code null-ok;} appropriate instance. + */ + public static LocalItem make(CstString name, CstType type, CstString signature) { + if (name == null && type == null) { + return null; } + return new LocalItem(name, type, signature); + } + /** * Constructs instance. * @@ -59,131 +59,131 @@ public class LocalItem implements Comparable<LocalItem> { * @param type {@code null-ok;} local variable type * @param signature {@code null-ok;} local variable signature */ - private LocalItem(CstString name, CstType type, CstString signature) { - this.name = name; - this.type = type; - this.signature = signature; + private LocalItem(CstString name, CstType type, CstString signature) { + this.name = name; + this.type = type; + this.signature = signature; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof LocalItem)) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof LocalItem)) { - return false; - } + LocalItem local = (LocalItem) other; - LocalItem local = (LocalItem) other; + return 0 == compareTo(local); + } - return 0 == compareTo(local); + /** + * Compares two strings like String.compareTo(), excepts treats a null + * as the least-possible string value. + * + * @return negative integer, zero, or positive integer in accordance + * with Comparable.compareTo() + */ + private static int compareHandlesNulls(CstString a, CstString b) { + if (a == b) { + return 0; + } else if (a == null) { + return -1; + } else if (b == null) { + return 1; + } else { + return a.compareTo(b); } + } - /** - * Compares two strings like String.compareTo(), excepts treats a null - * as the least-possible string value. - * - * @return negative integer, zero, or positive integer in accordance - * with Comparable.compareTo() - */ - private static int compareHandlesNulls(CstString a, CstString b) { - if (a == b) { - return 0; - } else if (a == null) { - return -1; - } else if (b == null) { - return 1; - } else { - return a.compareTo(b); - } + /** + * Compares two CstType like CstType.compareTo(), excepts treats a null + * as the least-possible string value. + * + * @return negative integer, zero, or positive integer in accordance + * with Comparable.compareTo() + */ + private static int compareHandlesNulls(CstType a, CstType b) { + if (a == b) { + return 0; + } else if (a == null) { + return -1; + } else if (b == null) { + return 1; + } else { + return a.compareTo(b); } + } - /** - * Compares two CstType like CstType.compareTo(), excepts treats a null - * as the least-possible string value. - * - * @return negative integer, zero, or positive integer in accordance - * with Comparable.compareTo() - */ - private static int compareHandlesNulls(CstType a, CstType b) { - if (a == b) { - return 0; - } else if (a == null) { - return -1; - } else if (b == null) { - return 1; - } else { - return a.compareTo(b); - } - } + /** {@inheritDoc} */ + @Override + public int compareTo(LocalItem local) { + int ret; - /** {@inheritDoc} */ - public int compareTo(LocalItem local) { - int ret; + ret = compareHandlesNulls(name, local.name); - ret = compareHandlesNulls(name, local.name); + if (ret != 0) { + return ret; + } - if (ret != 0) { - return ret; - } + ret = compareHandlesNulls(type, local.type); - ret = compareHandlesNulls(type, local.type); + if (ret != 0) { + return ret; + } - if (ret != 0) { - return ret; - } + ret = compareHandlesNulls(signature, local.signature); - ret = compareHandlesNulls(signature, local.signature); + return ret; + } - return ret; - } + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (name == null ? 0 : name.hashCode()) * 31 + (type == null ? 0 : type.hashCode()) + + (signature == null ? 0 : signature.hashCode()) * 17; + } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (name == null ? 0 : name.hashCode()) * 31 - + (type == null ? 0 : type.hashCode()) - + (signature == null ? 0 : signature.hashCode()) * 17; + /** {@inheritDoc} */ + @Override + public String toString() { + if (name != null && type == null && signature == null) { + return name.toQuoted(); + } else if (name == null && type == null && signature == null) { + return ""; } - /** {@inheritDoc} */ - @Override - public String toString() { - if (name != null && type == null && signature == null) { - return name.toQuoted(); - } else if (name == null && type == null && signature == null) { - return ""; - } - - return "[" + (name == null ? "" : name.toQuoted()) - + "|" + (type == null ? "" : type.getDescriptor().toQuoted()) - + "|" + (signature == null ? "" : signature.toQuoted()); - } + return "[" + (name == null ? "" : name.toQuoted()) + "|" + + (type == null ? "" : type.getDescriptor().toQuoted()) + "|" + + (signature == null ? "" : signature.toQuoted()); + } - /** - * Gets name. - * - * @return {@code null-ok;} name - */ - public CstString getName() { - return name; - } + /** + * Gets name. + * + * @return {@code null-ok;} name + */ + public CstString getName() { + return name; + } - /** - * Gets the signature that must be represented to successfully implement the source language's - * semantics (cf. the discussion under {@code dalvik.annotation.Signature} in "dex-format.html") - * - * @return signature - */ - public CstString getSignature() { - return signature; - } + /** + * Gets the signature that must be represented to successfully implement the source language's + * semantics (cf. the discussion under {@code dalvik.annotation.Signature} in "dex-format.html") + * + * @return signature + */ + public CstString getSignature() { + return signature; + } - /** - * Gets type. - * - * @return {@code null-ok;} type. - */ - public CstType getType() { - return type; - } + /** + * Gets type. + * + * @return {@code null-ok;} type. + */ + public CstType getType() { + return type; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/LocalVariableExtractor.java b/dx/src/com/android/jack/dx/rop/code/LocalVariableExtractor.java index 6420d38..739150c 100644 --- a/dx/src/com/android/jack/dx/rop/code/LocalVariableExtractor.java +++ b/dx/src/com/android/jack/dx/rop/code/LocalVariableExtractor.java @@ -24,168 +24,162 @@ import com.android.jack.dx.util.IntList; * a method. */ public final class LocalVariableExtractor { - /** {@code non-null;} method being extracted from */ - private final RopMethod method; - - /** {@code non-null;} block list for the method */ - private final BasicBlockList blocks; - - /** {@code non-null;} result in-progress */ - private final LocalVariableInfo resultInfo; - - /** {@code non-null;} work set indicating blocks needing to be processed */ - private final int[] workSet; - - /** - * Extracts out all the local variable information from the given method. - * - * @param method {@code non-null;} the method to extract from - * @return {@code non-null;} the extracted information - */ - public static LocalVariableInfo extract(RopMethod method) { - LocalVariableExtractor lve = new LocalVariableExtractor(method); - return lve.doit(); + /** {@code non-null;} method being extracted from */ + private final RopMethod method; + + /** {@code non-null;} block list for the method */ + private final BasicBlockList blocks; + + /** {@code non-null;} result in-progress */ + private final LocalVariableInfo resultInfo; + + /** {@code non-null;} work set indicating blocks needing to be processed */ + private final int[] workSet; + + /** + * Extracts out all the local variable information from the given method. + * + * @param method {@code non-null;} the method to extract from + * @return {@code non-null;} the extracted information + */ + public static LocalVariableInfo extract(RopMethod method) { + LocalVariableExtractor lve = new LocalVariableExtractor(method); + return lve.doit(); + } + + /** + * Constructs an instance. This method is private. Use {@link #extract}. + * + * @param method {@code non-null;} the method to extract from + */ + private LocalVariableExtractor(RopMethod method) { + if (method == null) { + throw new NullPointerException("method == null"); } - /** - * Constructs an instance. This method is private. Use {@link #extract}. - * - * @param method {@code non-null;} the method to extract from - */ - private LocalVariableExtractor(RopMethod method) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - BasicBlockList blocks = method.getBlocks(); - int maxLabel = blocks.getMaxLabel(); - - this.method = method; - this.blocks = blocks; - this.resultInfo = new LocalVariableInfo(method); - this.workSet = Bits.makeBitSet(maxLabel); + BasicBlockList blocks = method.getBlocks(); + int maxLabel = blocks.getMaxLabel(); + + this.method = method; + this.blocks = blocks; + this.resultInfo = new LocalVariableInfo(method); + this.workSet = Bits.makeBitSet(maxLabel); + } + + /** + * Does the extraction. + * + * @return {@code non-null;} the extracted information + */ + private LocalVariableInfo doit() { + for (int label = method.getFirstLabel(); label >= 0; label = Bits.findFirst(workSet, 0)) { + Bits.clear(workSet, label); + processBlock(label); } - /** - * Does the extraction. - * - * @return {@code non-null;} the extracted information + resultInfo.setImmutable(); + return resultInfo; + } + + /** + * Processes a single block. + * + * @param label {@code >= 0;} label of the block to process + */ + private void processBlock(int label) { + RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(label); + BasicBlock block = blocks.labelToBlock(label); + InsnList insns = block.getInsns(); + int insnSz = insns.size(); + + /* + * We may have to treat the last instruction specially: If it + * can (but doesn't always) throw, and the exception can be + * caught within the same method, then we need to use the + * state *before* executing it to be what is merged into + * exception targets. + */ + boolean canThrowDuringLastInsn = + block.hasExceptionHandlers() && (insns.getLast().getResult() != null); + int freezeSecondaryStateAt = insnSz - 1; + RegisterSpecSet secondaryState = primaryState; + + /* + * Iterate over the instructions, adding information for each place + * that the active variable set changes. */ - private LocalVariableInfo doit() { - for (int label = method.getFirstLabel(); - label >= 0; - label = Bits.findFirst(workSet, 0)) { - Bits.clear(workSet, label); - processBlock(label); - } - resultInfo.setImmutable(); - return resultInfo; - } +for (int i = 0; i < insnSz; i++) { + if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) { + // Until this point, primaryState == secondaryState. + primaryState.setImmutable(); + primaryState = primaryState.mutableCopy(); + } - /** - * Processes a single block. - * - * @param label {@code >= 0;} label of the block to process - */ - private void processBlock(int label) { - RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(label); - BasicBlock block = blocks.labelToBlock(label); - InsnList insns = block.getInsns(); - int insnSz = insns.size(); + Insn insn = insns.get(i); + RegisterSpec result; + + result = insn.getLocalAssignment(); + if (result == null) { /* - * We may have to treat the last instruction specially: If it - * can (but doesn't always) throw, and the exception can be - * caught within the same method, then we need to use the - * state *before* executing it to be what is merged into - * exception targets. + * If an assignment assigns over an existing local, make + * sure to mark the local as going out of scope. */ - boolean canThrowDuringLastInsn = block.hasExceptionHandlers() && - (insns.getLast().getResult() != null); - int freezeSecondaryStateAt = insnSz - 1; - RegisterSpecSet secondaryState = primaryState; +result = insn.getResult(); + + if (result != null && primaryState.get(result.getReg()) != null) { + primaryState.remove(primaryState.get(result.getReg())); + } + continue; + } + + result = result.withSimpleType(); + + RegisterSpec already = primaryState.get(result); + /* + * The equals() check ensures we only add new info if + * the instruction causes a change to the set of + * active variables. + */ + if (!result.equals(already)) { /* - * Iterate over the instructions, adding information for each place - * that the active variable set changes. + * If this insn represents a local moving from one register + * to another, remove the association between the old register + * and the local. */ + RegisterSpec previous = primaryState.localItemToSpec(result.getLocalItem()); + + if (previous != null && (previous.getReg() != result.getReg())) { - for (int i = 0; i < insnSz; i++) { - if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) { - // Until this point, primaryState == secondaryState. - primaryState.setImmutable(); - primaryState = primaryState.mutableCopy(); - } - - Insn insn = insns.get(i); - RegisterSpec result; - - result = insn.getLocalAssignment(); - - if (result == null) { - /* - * If an assignment assigns over an existing local, make - * sure to mark the local as going out of scope. - */ - - result = insn.getResult(); - - if (result != null - && primaryState.get(result.getReg()) != null) { - primaryState.remove(primaryState.get(result.getReg())); - } - continue; - } - - result = result.withSimpleType(); - - RegisterSpec already = primaryState.get(result); - /* - * The equals() check ensures we only add new info if - * the instruction causes a change to the set of - * active variables. - */ - if (!result.equals(already)) { - /* - * If this insn represents a local moving from one register - * to another, remove the association between the old register - * and the local. - */ - RegisterSpec previous - = primaryState.localItemToSpec(result.getLocalItem()); - - if (previous != null - && (previous.getReg() != result.getReg())) { - - primaryState.remove(previous); - } - - resultInfo.addAssignment(insn, result); - primaryState.put(result); - } + primaryState.remove(previous); } - primaryState.setImmutable(); + resultInfo.addAssignment(insn, result); + primaryState.put(result); + } + } - /* - * Merge this state into the start state for each successor, - * and update the work set where required (that is, in cases - * where the start state for a block changes). - */ + primaryState.setImmutable(); - IntList successors = block.getSuccessors(); - int succSz = successors.size(); - int primarySuccessor = block.getPrimarySuccessor(); + /* + * Merge this state into the start state for each successor, + * and update the work set where required (that is, in cases + * where the start state for a block changes). + */ - for (int i = 0; i < succSz; i++) { - int succ = successors.get(i); - RegisterSpecSet state = (succ == primarySuccessor) ? - primaryState : secondaryState; +IntList successors = block.getSuccessors(); + int succSz = successors.size(); + int primarySuccessor = block.getPrimarySuccessor(); - if (resultInfo.mergeStarts(succ, state)) { - Bits.set(workSet, succ); - } - } + for (int i = 0; i < succSz; i++) { + int succ = successors.get(i); + RegisterSpecSet state = (succ == primarySuccessor) ? primaryState : secondaryState; + + if (resultInfo.mergeStarts(succ, state)) { + Bits.set(workSet, succ); + } } + } } diff --git a/dx/src/com/android/jack/dx/rop/code/LocalVariableInfo.java b/dx/src/com/android/jack/dx/rop/code/LocalVariableInfo.java index a15402f..f476154 100644 --- a/dx/src/com/android/jack/dx/rop/code/LocalVariableInfo.java +++ b/dx/src/com/android/jack/dx/rop/code/LocalVariableInfo.java @@ -16,7 +16,6 @@ package com.android.jack.dx.rop.code; -import com.android.jack.dx.rop.type.TypeBearer; import com.android.jack.dx.util.MutabilityControl; import java.util.HashMap; @@ -25,230 +24,226 @@ import java.util.HashMap; * Container for local variable information for a particular {@link * RopMethod}. */ -public final class LocalVariableInfo - extends MutabilityControl { - /** {@code >= 0;} the register count for the method */ - private final int regCount; - - /** - * {@code non-null;} {@link RegisterSpecSet} to use when indicating a block - * that has no locals; it is empty and immutable but has an appropriate - * max size for the method - */ - private final RegisterSpecSet emptySet; - - /** - * {@code non-null;} array consisting of register sets representing the - * sets of variables already assigned upon entry to each block, - * where array indices correspond to block labels - */ - private final RegisterSpecSet[] blockStarts; - - /** {@code non-null;} map from instructions to the variable each assigns */ - private final HashMap<Insn, RegisterSpec> insnAssignments; - - /** - * Constructs an instance. - * - * @param method {@code non-null;} the method being represented by this instance - */ - public LocalVariableInfo(RopMethod method) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - BasicBlockList blocks = method.getBlocks(); - int maxLabel = blocks.getMaxLabel(); - - this.regCount = blocks.getRegCount(); - this.emptySet = new RegisterSpecSet(regCount); - this.blockStarts = new RegisterSpecSet[maxLabel]; - this.insnAssignments = - new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount()); - - emptySet.setImmutable(); +public final class LocalVariableInfo extends MutabilityControl { + /** {@code >= 0;} the register count for the method */ + private final int regCount; + + /** + * {@code non-null;} {@link RegisterSpecSet} to use when indicating a block + * that has no locals; it is empty and immutable but has an appropriate + * max size for the method + */ + private final RegisterSpecSet emptySet; + + /** + * {@code non-null;} array consisting of register sets representing the + * sets of variables already assigned upon entry to each block, + * where array indices correspond to block labels + */ + private final RegisterSpecSet[] blockStarts; + + /** {@code non-null;} map from instructions to the variable each assigns */ + private final HashMap<Insn, RegisterSpec> insnAssignments; + + /** + * Constructs an instance. + * + * @param method {@code non-null;} the method being represented by this instance + */ + public LocalVariableInfo(RopMethod method) { + if (method == null) { + throw new NullPointerException("method == null"); } - /** - * Sets the register set associated with the start of the block with - * the given label. - * - * @param label {@code >= 0;} the block label - * @param specs {@code non-null;} the register set to associate with the block - */ - public void setStarts(int label, RegisterSpecSet specs) { - throwIfImmutable(); - - if (specs == null) { - throw new NullPointerException("specs == null"); - } - - try { - blockStarts[label] = specs; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("bogus label"); - } + BasicBlockList blocks = method.getBlocks(); + int maxLabel = blocks.getMaxLabel(); + + this.regCount = blocks.getRegCount(); + this.emptySet = new RegisterSpecSet(regCount); + this.blockStarts = new RegisterSpecSet[maxLabel]; + this.insnAssignments = new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount()); + + emptySet.setImmutable(); + } + + /** + * Sets the register set associated with the start of the block with + * the given label. + * + * @param label {@code >= 0;} the block label + * @param specs {@code non-null;} the register set to associate with the block + */ + public void setStarts(int label, RegisterSpecSet specs) { + throwIfImmutable(); + + if (specs == null) { + throw new NullPointerException("specs == null"); } - /** - * Merges the given register set into the set for the block with the - * given label. If there was not already an associated set, then this - * is the same as calling {@link #setStarts}. Otherwise, this will - * merge the two sets and call {@link #setStarts} on the result of the - * merge. - * - * @param label {@code >= 0;} the block label - * @param specs {@code non-null;} the register set to merge into the start set - * for the block - * @return {@code true} if the merge resulted in an actual change - * to the associated set (including storing one for the first time) or - * {@code false} if there was no change - */ - public boolean mergeStarts(int label, RegisterSpecSet specs) { - RegisterSpecSet start = getStarts0(label); - boolean changed = false; - - if (start == null) { - setStarts(label, specs); - return true; - } - - RegisterSpecSet newStart = start.mutableCopy(); - if (start.size() != 0) { - newStart.intersect(specs, true); - } else { - newStart = specs.mutableCopy(); - } - - if (start.equals(newStart)) { - return false; - } - - newStart.setImmutable(); - setStarts(label, newStart); - - return true; + try { + blockStarts[label] = specs; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("bogus label"); } - - /** - * Gets the register set associated with the start of the block - * with the given label. This returns an empty set with the appropriate - * max size if no set was associated with the block in question. - * - * @param label {@code >= 0;} the block label - * @return {@code non-null;} the associated register set - */ - public RegisterSpecSet getStarts(int label) { - RegisterSpecSet result = getStarts0(label); - - return (result != null) ? result : emptySet; - } - - /** - * Gets the register set associated with the start of the given - * block. This is just convenient shorthand for - * {@code getStarts(block.getLabel())}. - * - * @param block {@code non-null;} the block in question - * @return {@code non-null;} the associated register set - */ - public RegisterSpecSet getStarts(BasicBlock block) { - return getStarts(block.getLabel()); + } + + /** + * Merges the given register set into the set for the block with the + * given label. If there was not already an associated set, then this + * is the same as calling {@link #setStarts}. Otherwise, this will + * merge the two sets and call {@link #setStarts} on the result of the + * merge. + * + * @param label {@code >= 0;} the block label + * @param specs {@code non-null;} the register set to merge into the start set + * for the block + * @return {@code true} if the merge resulted in an actual change + * to the associated set (including storing one for the first time) or + * {@code false} if there was no change + */ + public boolean mergeStarts(int label, RegisterSpecSet specs) { + RegisterSpecSet start = getStarts0(label); + + if (start == null) { + setStarts(label, specs); + return true; } - /** - * Gets a mutable copy of the register set associated with the - * start of the block with the given label. This returns a - * newly-allocated empty {@link RegisterSpecSet} of appropriate - * max size if there is not yet any set associated with the block. - * - * @param label {@code >= 0;} the block label - * @return {@code non-null;} the associated register set - */ - public RegisterSpecSet mutableCopyOfStarts(int label) { - RegisterSpecSet result = getStarts0(label); - - return (result != null) ? - result.mutableCopy() : new RegisterSpecSet(regCount); + RegisterSpecSet newStart = start.mutableCopy(); + if (start.size() != 0) { + newStart.intersect(specs, true); + } else { + newStart = specs.mutableCopy(); } - /** - * Adds an assignment association for the given instruction and - * register spec. This throws an exception if the instruction - * doesn't actually perform a named variable assignment. - * - * <b>Note:</b> Although the instruction contains its own spec for - * the result, it still needs to be passed in explicitly to this - * method, since the spec that is stored here should always have a - * simple type and the one in the instruction can be an arbitrary - * {@link TypeBearer} (such as a constant value). - * - * @param insn {@code non-null;} the instruction in question - * @param spec {@code non-null;} the associated register spec - */ - public void addAssignment(Insn insn, RegisterSpec spec) { - throwIfImmutable(); - - if (insn == null) { - throw new NullPointerException("insn == null"); - } - - if (spec == null) { - throw new NullPointerException("spec == null"); - } - - insnAssignments.put(insn, spec); + if (start.equals(newStart)) { + return false; } - /** - * Gets the named register being assigned by the given instruction, if - * previously stored in this instance. - * - * @param insn {@code non-null;} instruction in question - * @return {@code null-ok;} the named register being assigned, if any - */ - public RegisterSpec getAssignment(Insn insn) { - return insnAssignments.get(insn); + newStart.setImmutable(); + setStarts(label, newStart); + + return true; + } + + /** + * Gets the register set associated with the start of the block + * with the given label. This returns an empty set with the appropriate + * max size if no set was associated with the block in question. + * + * @param label {@code >= 0;} the block label + * @return {@code non-null;} the associated register set + */ + public RegisterSpecSet getStarts(int label) { + RegisterSpecSet result = getStarts0(label); + + return (result != null) ? result : emptySet; + } + + /** + * Gets the register set associated with the start of the given + * block. This is just convenient shorthand for + * {@code getStarts(block.getLabel())}. + * + * @param block {@code non-null;} the block in question + * @return {@code non-null;} the associated register set + */ + public RegisterSpecSet getStarts(BasicBlock block) { + return getStarts(block.getLabel()); + } + + /** + * Gets a mutable copy of the register set associated with the + * start of the block with the given label. This returns a + * newly-allocated empty {@link RegisterSpecSet} of appropriate + * max size if there is not yet any set associated with the block. + * + * @param label {@code >= 0;} the block label + * @return {@code non-null;} the associated register set + */ + public RegisterSpecSet mutableCopyOfStarts(int label) { + RegisterSpecSet result = getStarts0(label); + + return (result != null) ? result.mutableCopy() : new RegisterSpecSet(regCount); + } + + /** + * Adds an assignment association for the given instruction and + * register spec. This throws an exception if the instruction + * doesn't actually perform a named variable assignment. + * + * <b>Note:</b> Although the instruction contains its own spec for + * the result, it still needs to be passed in explicitly to this + * method, since the spec that is stored here should always have a + * simple type and the one in the instruction can be an arbitrary + * {@link TypeBearer} (such as a constant value). + * + * @param insn {@code non-null;} the instruction in question + * @param spec {@code non-null;} the associated register spec + */ + public void addAssignment(Insn insn, RegisterSpec spec) { + throwIfImmutable(); + + if (insn == null) { + throw new NullPointerException("insn == null"); } - /** - * Gets the number of assignments recorded by this instance. - * - * @return {@code >= 0;} the number of assignments - */ - public int getAssignmentCount() { - return insnAssignments.size(); + if (spec == null) { + throw new NullPointerException("spec == null"); } - public void debugDump() { - for (int label = 0 ; label < blockStarts.length; label++) { - if (blockStarts[label] == null) { - continue; - } - - if (blockStarts[label] == emptySet) { - System.out.printf("%04x: empty set\n", label); - } else { - System.out.printf("%04x: %s\n", label, blockStarts[label]); - } - } + insnAssignments.put(insn, spec); + } + + /** + * Gets the named register being assigned by the given instruction, if + * previously stored in this instance. + * + * @param insn {@code non-null;} instruction in question + * @return {@code null-ok;} the named register being assigned, if any + */ + public RegisterSpec getAssignment(Insn insn) { + return insnAssignments.get(insn); + } + + /** + * Gets the number of assignments recorded by this instance. + * + * @return {@code >= 0;} the number of assignments + */ + public int getAssignmentCount() { + return insnAssignments.size(); + } + + public void debugDump() { + for (int label = 0; label < blockStarts.length; label++) { + if (blockStarts[label] == null) { + continue; + } + + if (blockStarts[label] == emptySet) { + System.out.printf("%04x: empty set\n", label); + } else { + System.out.printf("%04x: %s\n", label, blockStarts[label]); + } } - - /** - * Helper method, to get the starts for a label, throwing the - * right exception for range problems. - * - * @param label {@code >= 0;} the block label - * @return {@code null-ok;} associated register set or {@code null} if there - * is none - */ - private RegisterSpecSet getStarts0(int label) { - try { - return blockStarts[label]; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("bogus label"); - } + } + + /** + * Helper method, to get the starts for a label, throwing the + * right exception for range problems. + * + * @param label {@code >= 0;} the block label + * @return {@code null-ok;} associated register set or {@code null} if there + * is none + */ + private RegisterSpecSet getStarts0(int label) { + try { + return blockStarts[label]; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("bogus label"); } + } } diff --git a/dx/src/com/android/jack/dx/rop/code/PlainCstInsn.java b/dx/src/com/android/jack/dx/rop/code/PlainCstInsn.java index 597b92d..2220d4a 100644 --- a/dx/src/com/android/jack/dx/rop/code/PlainCstInsn.java +++ b/dx/src/com/android/jack/dx/rop/code/PlainCstInsn.java @@ -25,63 +25,55 @@ import com.android.jack.dx.rop.type.TypeList; * Instruction which contains an explicit reference to a constant * but which cannot throw an exception. */ -public final class PlainCstInsn - extends CstInsn { - /** - * Constructs an instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param result {@code null-ok;} spec for the result, if any - * @param sources {@code non-null;} specs for all the sources - * @param cst {@code non-null;} the constant - */ - public PlainCstInsn(Rop opcode, SourcePosition position, - RegisterSpec result, RegisterSpecList sources, - Constant cst) { - super(opcode, position, result, sources, cst); +public final class PlainCstInsn extends CstInsn { + /** + * Constructs an instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param result {@code null-ok;} spec for the result, if any + * @param sources {@code non-null;} specs for all the sources + * @param cst {@code non-null;} the constant + */ + public PlainCstInsn(Rop opcode, SourcePosition position, RegisterSpec result, + RegisterSpecList sources, Constant cst) { + super(opcode, position, result, sources, cst); - if (opcode.getBranchingness() != Rop.BRANCH_NONE) { - throw new IllegalArgumentException("bogus branchingness"); - } + if (opcode.getBranchingness() != Rop.BRANCH_NONE) { + throw new IllegalArgumentException("bogus branchingness"); } + } - /** {@inheritDoc} */ - @Override - public TypeList getCatches() { - return StdTypeList.EMPTY; - } + /** {@inheritDoc} */ + @Override + public TypeList getCatches() { + return StdTypeList.EMPTY; + } - /** {@inheritDoc} */ - @Override - public void accept(Visitor visitor) { - visitor.visitPlainCstInsn(this); - } + /** {@inheritDoc} */ + @Override + public void accept(Visitor visitor) { + visitor.visitPlainCstInsn(this); + } - /** {@inheritDoc} */ - @Override - public Insn withAddedCatch(Type type) { - throw new UnsupportedOperationException("unsupported"); - } + /** {@inheritDoc} */ + @Override + public Insn withAddedCatch(Type type) { + throw new UnsupportedOperationException("unsupported"); + } - /** {@inheritDoc} */ - @Override - public Insn withRegisterOffset(int delta) { - return new PlainCstInsn(getOpcode(), getPosition(), - getResult().withOffset(delta), - getSources().withOffset(delta), - getConstant()); - } + /** {@inheritDoc} */ + @Override + public Insn withRegisterOffset(int delta) { + return new PlainCstInsn(getOpcode(), getPosition(), getResult().withOffset(delta), + getSources().withOffset(delta), getConstant()); + } - /** {@inheritDoc} */ - @Override - public Insn withNewRegisters(RegisterSpec result, - RegisterSpecList sources) { + /** {@inheritDoc} */ + @Override + public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) { - return new PlainCstInsn(getOpcode(), getPosition(), - result, - sources, - getConstant()); + return new PlainCstInsn(getOpcode(), getPosition(), result, sources, getConstant()); - } + } } diff --git a/dx/src/com/android/jack/dx/rop/code/PlainInsn.java b/dx/src/com/android/jack/dx/rop/code/PlainInsn.java index a674537..3a31840 100644 --- a/dx/src/com/android/jack/dx/rop/code/PlainInsn.java +++ b/dx/src/com/android/jack/dx/rop/code/PlainInsn.java @@ -27,131 +27,121 @@ import com.android.jack.dx.rop.type.TypeList; * Plain instruction, which has no embedded data and which cannot possibly * throw an exception. */ -public final class PlainInsn - extends Insn { - /** - * Constructs an instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param result {@code null-ok;} spec for the result, if any - * @param sources {@code non-null;} specs for all the sources - */ - public PlainInsn(Rop opcode, SourcePosition position, - RegisterSpec result, RegisterSpecList sources) { - super(opcode, position, result, sources); - - switch (opcode.getBranchingness()) { - case Rop.BRANCH_SWITCH: - case Rop.BRANCH_THROW: { - throw new IllegalArgumentException("bogus branchingness"); - } - } - - if (result != null && opcode.getBranchingness() != Rop.BRANCH_NONE) { - // move-result-pseudo is required here - throw new IllegalArgumentException - ("can't mix branchingness with result"); - } +public final class PlainInsn extends Insn { + /** + * Constructs an instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param result {@code null-ok;} spec for the result, if any + * @param sources {@code non-null;} specs for all the sources + */ + public PlainInsn(Rop opcode, SourcePosition position, RegisterSpec result, + RegisterSpecList sources) { + super(opcode, position, result, sources); + + switch (opcode.getBranchingness()) { + case Rop.BRANCH_SWITCH: + case Rop.BRANCH_THROW: { + throw new IllegalArgumentException("bogus branchingness"); + } } - /** - * Constructs a single-source instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param result {@code null-ok;} spec for the result, if any - * @param source {@code non-null;} spec for the source - */ - public PlainInsn(Rop opcode, SourcePosition position, RegisterSpec result, - RegisterSpec source) { - this(opcode, position, result, RegisterSpecList.make(source)); + if (result != null && opcode.getBranchingness() != Rop.BRANCH_NONE) { + // move-result-pseudo is required here + throw new IllegalArgumentException("can't mix branchingness with result"); } - - /** {@inheritDoc} */ - @Override - public TypeList getCatches() { - return StdTypeList.EMPTY; + } + + /** + * Constructs a single-source instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param result {@code null-ok;} spec for the result, if any + * @param source {@code non-null;} spec for the source + */ + public PlainInsn(Rop opcode, SourcePosition position, RegisterSpec result, RegisterSpec source) { + this(opcode, position, result, RegisterSpecList.make(source)); + } + + /** {@inheritDoc} */ + @Override + public TypeList getCatches() { + return StdTypeList.EMPTY; + } + + /** {@inheritDoc} */ + @Override + public void accept(Visitor visitor) { + visitor.visitPlainInsn(this); + } + + /** {@inheritDoc} */ + @Override + public Insn withAddedCatch(Type type) { + throw new UnsupportedOperationException("unsupported"); + } + + /** {@inheritDoc} */ + @Override + public Insn withRegisterOffset(int delta) { + return new PlainInsn(getOpcode(), getPosition(), getResult().withOffset(delta), + getSources().withOffset(delta)); + } + + /** {@inheritDoc} */ + @Override + public Insn withSourceLiteral() { + RegisterSpecList sources = getSources(); + int szSources = sources.size(); + + if (szSources == 0) { + return this; } - /** {@inheritDoc} */ - @Override - public void accept(Visitor visitor) { - visitor.visitPlainInsn(this); - } - - /** {@inheritDoc} */ - @Override - public Insn withAddedCatch(Type type) { - throw new UnsupportedOperationException("unsupported"); - } - - /** {@inheritDoc} */ - @Override - public Insn withRegisterOffset(int delta) { - return new PlainInsn(getOpcode(), getPosition(), - getResult().withOffset(delta), - getSources().withOffset(delta)); - } - - /** {@inheritDoc} */ - @Override - public Insn withSourceLiteral() { - RegisterSpecList sources = getSources(); - int szSources = sources.size(); - - if (szSources == 0) { - return this; + TypeBearer lastType = sources.get(szSources - 1).getTypeBearer(); + + if (!lastType.isConstant()) { + // Check for reverse subtraction, where first source is constant + TypeBearer firstType = sources.get(0).getTypeBearer(); + if (szSources == 2 && firstType.isConstant()) { + Constant cst = (Constant) firstType; + RegisterSpecList newSources = sources.withoutFirst(); + Rop newRop = Rops.ropFor(getOpcode().getOpcode(), getResult(), newSources, cst); + return new PlainCstInsn(newRop, getPosition(), getResult(), newSources, cst); + } + return this; + } else { + + Constant cst = (Constant) lastType; + + RegisterSpecList newSources = sources.withoutLast(); + + Rop newRop; + try { + // Check for constant subtraction and flip it to be addition + int opcode = getOpcode().getOpcode(); + if (opcode == RegOps.SUB && cst instanceof CstInteger) { + opcode = RegOps.ADD; + cst = CstInteger.make(-((CstInteger) cst).getValue()); } + newRop = Rops.ropFor(opcode, getResult(), newSources, cst); + } catch (IllegalArgumentException ex) { + // There's no rop for this case + return this; + } - TypeBearer lastType = sources.get(szSources - 1).getTypeBearer(); - - if (!lastType.isConstant()) { - // Check for reverse subtraction, where first source is constant - TypeBearer firstType = sources.get(0).getTypeBearer(); - if (szSources == 2 && firstType.isConstant()) { - Constant cst = (Constant) firstType; - RegisterSpecList newSources = sources.withoutFirst(); - Rop newRop = Rops.ropFor(getOpcode().getOpcode(), getResult(), - newSources, cst); - return new PlainCstInsn(newRop, getPosition(), getResult(), - newSources, cst); - } - return this; - } else { - - Constant cst = (Constant) lastType; - - RegisterSpecList newSources = sources.withoutLast(); - - Rop newRop; - try { - // Check for constant subtraction and flip it to be addition - int opcode = getOpcode().getOpcode(); - if (opcode == RegOps.SUB && cst instanceof CstInteger) { - opcode = RegOps.ADD; - cst = CstInteger.make(-((CstInteger)cst).getValue()); - } - newRop = Rops.ropFor(opcode, getResult(), newSources, cst); - } catch (IllegalArgumentException ex) { - // There's no rop for this case - return this; - } - - return new PlainCstInsn(newRop, getPosition(), - getResult(), newSources, cst); - } + return new PlainCstInsn(newRop, getPosition(), getResult(), newSources, cst); } + } - /** {@inheritDoc} */ - @Override - public Insn withNewRegisters(RegisterSpec result, - RegisterSpecList sources) { + /** {@inheritDoc} */ + @Override + public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) { - return new PlainInsn(getOpcode(), getPosition(), - result, - sources); + return new PlainInsn(getOpcode(), getPosition(), result, sources); - } + } } diff --git a/dx/src/com/android/jack/dx/rop/code/RegOps.java b/dx/src/com/android/jack/dx/rop/code/RegOps.java index a863a89..4587a04 100644 --- a/dx/src/com/android/jack/dx/rop/code/RegOps.java +++ b/dx/src/com/android/jack/dx/rop/code/RegOps.java @@ -29,371 +29,427 @@ import com.android.jack.dx.util.Hex; * each of the values. */ public final class RegOps { - /** {@code nop()} */ - public static final int NOP = 1; + /** {@code nop()} */ + public static final int NOP = 1; - /** {@code T: any type; r,x: T :: r = x;} */ - public static final int MOVE = 2; + /** {@code T: any type; r,x: T :: r = x;} */ + public static final int MOVE = 2; - /** {@code T: any type; r,param(x): T :: r = param(x)} */ - public static final int MOVE_PARAM = 3; + /** {@code T: any type; r,param(x): T :: r = param(x)} */ + public static final int MOVE_PARAM = 3; - /** - * {@code T: Throwable; r: T :: r = caught_exception}. - * <b>Note:</b> This opcode should only ever be used in the - * first instruction of a block, and such blocks must be - * the start of an exception handler. - */ - public static final int MOVE_EXCEPTION = 4; - - /** {@code T: any type; r, literal: T :: r = literal;} */ - public static final int CONST = 5; - - /** {@code goto label} */ - public static final int GOTO = 6; - - /** - * {@code T: int or Object; x,y: T :: if (x == y) goto - * label} - */ - public static final int IF_EQ = 7; - - /** - * {@code T: int or Object; x,y: T :: if (x != y) goto - * label} - */ - public static final int IF_NE = 8; - - /** {@code x,y: int :: if (x < y) goto label} */ - public static final int IF_LT = 9; - - /** {@code x,y: int :: if (x >= y) goto label} */ - public static final int IF_GE = 10; - - /** {@code x,y: int :: if (x <= y) goto label} */ - public static final int IF_LE = 11; - - /** {@code x,y: int :: if (x > y) goto label} */ - public static final int IF_GT = 12; - - /** {@code x: int :: goto table[x]} */ - public static final int SWITCH = 13; - - /** {@code T: any numeric type; r,x,y: T :: r = x + y} */ - public static final int ADD = 14; - - /** {@code T: any numeric type; r,x,y: T :: r = x - y} */ - public static final int SUB = 15; - - /** {@code T: any numeric type; r,x,y: T :: r = x * y} */ - public static final int MUL = 16; - - /** {@code T: any numeric type; r,x,y: T :: r = x / y} */ - public static final int DIV = 17; - - /** - * {@code T: any numeric type; r,x,y: T :: r = x % y} - * (Java-style remainder) - */ - public static final int REM = 18; - - /** {@code T: any numeric type; r,x: T :: r = -x} */ - public static final int NEG = 19; - - /** {@code T: any integral type; r,x,y: T :: r = x & y} */ - public static final int AND = 20; - - /** {@code T: any integral type; r,x,y: T :: r = x | y} */ - public static final int OR = 21; - - /** {@code T: any integral type; r,x,y: T :: r = x ^ y} */ - public static final int XOR = 22; - - /** - * {@code T: any integral type; r,x: T; y: int :: r = x << y} - */ - public static final int SHL = 23; - - /** - * {@code T: any integral type; r,x: T; y: int :: r = x >> y} - * (signed right-shift) - */ - public static final int SHR = 24; - - /** - * {@code T: any integral type; r,x: T; y: int :: r = x >>> y} - * (unsigned right-shift) - */ - public static final int USHR = 25; - - /** {@code T: any integral type; r,x: T :: r = ~x} */ - public static final int NOT = 26; - - /** - * {@code T: any numeric type; r: int; x,y: T :: r = (x == y) ? 0 - * : (x > y) ? 1 : -1} (Java-style "cmpl" where a NaN is - * considered "less than" all other values; also used for integral - * comparisons) - */ - public static final int CMPL = 27; - - /** - * {@code T: any floating point type; r: int; x,y: T :: r = (x == y) ? 0 - * : (x < y) ? -1 : 1} (Java-style "cmpg" where a NaN is - * considered "greater than" all other values) - */ - public static final int CMPG = 28; - - /** - * {@code T: any numeric type; U: any numeric type; r: T; x: U :: - * r = (T) x} (numeric type conversion between the four - * "real" numeric types) - */ - public static final int CONV = 29; - - /** - * {@code r,x: int :: r = (x << 24) >> 24} (Java-style - * convert int to byte) - */ - public static final int TO_BYTE = 30; - - /** - * {@code r,x: int :: r = x & 0xffff} (Java-style convert int to char) - */ - public static final int TO_CHAR = 31; - - /** - * {@code r,x: int :: r = (x << 16) >> 16} (Java-style - * convert int to short) - */ - public static final int TO_SHORT = 32; - - /** {@code T: return type for the method; x: T; return x} */ - public static final int RETURN = 33; - - /** {@code T: any type; r: int; x: T[]; :: r = x.length} */ - public static final int ARRAY_LENGTH = 34; - - /** {@code x: Throwable :: throw(x)} */ - public static final int THROW = 35; - - /** {@code x: Object :: monitorenter(x)} */ - public static final int MONITOR_ENTER = 36; - - /** {@code x: Object :: monitorexit(x)} */ - public static final int MONITOR_EXIT = 37; - - /** {@code T: any type; r: T; x: T[]; y: int :: r = x[y]} */ - public static final int AGET = 38; - - /** {@code T: any type; x: T; y: T[]; z: int :: x[y] = z} */ - public static final int APUT = 39; - - /** - * {@code T: any non-array object type :: r = - * alloc(T)} (allocate heap space for an object) - */ - public static final int NEW_INSTANCE = 40; - - /** {@code T: any array type; r: T; x: int :: r = new T[x]} */ - public static final int NEW_ARRAY = 41; - - /** - * {@code T: any array type; r: T; x: int; v0..vx: T :: r = new T[x] - * {v0, ..., vx}} - */ - public static final int FILLED_NEW_ARRAY = 42; - - /** - * {@code T: any object type; x: Object :: (T) x} (can - * throw {@code ClassCastException}) - */ - public static final int CHECK_CAST = 43; - - /** - * {@code T: any object type; x: Object :: x instanceof T} - */ - public static final int INSTANCE_OF = 44; - - /** - * {@code T: any type; r: T; x: Object; f: instance field spec of - * type T :: r = x.f} - */ - public static final int GET_FIELD = 45; - - /** - * {@code T: any type; r: T; f: static field spec of type T :: r = - * f} - */ - public static final int GET_STATIC = 46; - - /** - * {@code T: any type; x: T; y: Object; f: instance field spec of type - * T :: y.f = x} - */ - public static final int PUT_FIELD = 47; - - /** - * {@code T: any type; f: static field spec of type T; x: T :: f = x} - */ - public static final int PUT_STATIC = 48; - - /** - * {@code Tr, T0, T1...: any types; r: Tr; m: static method spec; - * y0: T0; y1: T1 ... :: r = m(y0, y1, ...)} (call static - * method) - */ - public static final int INVOKE_STATIC = 49; - - /** - * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method - * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call normal - * virtual method) - */ - public static final int INVOKE_VIRTUAL = 50; - - /** - * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method - * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call - * superclass virtual method) - */ - public static final int INVOKE_SUPER = 51; - - /** - * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method - * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call - * direct/special method) - */ - public static final int INVOKE_DIRECT = 52; - - /** - * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: interface - * (instance) method spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, - * ...)} (call interface method) - */ - public static final int INVOKE_INTERFACE = 53; - - /** - * {@code T0: any type; name: local variable name :: mark(name,T0)} - * (mark beginning or end of local variable name) - */ - public static final int MARK_LOCAL = 54; - - /** - * {@code T: Any type; r: T :: r = return_type}. - * <b>Note:</b> This opcode should only ever be used in the - * first instruction of a block following an invoke-*. - */ - public static final int MOVE_RESULT = 55; - - /** - * {@code T: Any type; r: T :: r = return_type}. - * <b>Note:</b> This opcode should only ever be used in the - * first instruction of a block following a non-invoke throwing insn - */ - public static final int MOVE_RESULT_PSEUDO = 56; - - /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */ - public static final int FILL_ARRAY_DATA = 57; - - /** - * This class is uninstantiable. - */ - private RegOps() { - // This space intentionally left blank. + /** + * {@code T: Throwable; r: T :: r = caught_exception}. + * <b>Note:</b> This opcode should only ever be used in the + * first instruction of a block, and such blocks must be + * the start of an exception handler. + */ + public static final int MOVE_EXCEPTION = 4; + + /** {@code T: any type; r, literal: T :: r = literal;} */ + public static final int CONST = 5; + + /** {@code goto label} */ + public static final int GOTO = 6; + + /** + * {@code T: int or Object; x,y: T :: if (x == y) goto + * label} + */ + public static final int IF_EQ = 7; + + /** + * {@code T: int or Object; x,y: T :: if (x != y) goto + * label} + */ + public static final int IF_NE = 8; + + /** {@code x,y: int :: if (x < y) goto label} */ + public static final int IF_LT = 9; + + /** {@code x,y: int :: if (x >= y) goto label} */ + public static final int IF_GE = 10; + + /** {@code x,y: int :: if (x <= y) goto label} */ + public static final int IF_LE = 11; + + /** {@code x,y: int :: if (x > y) goto label} */ + public static final int IF_GT = 12; + + /** {@code x: int :: goto table[x]} */ + public static final int SWITCH = 13; + + /** {@code T: any numeric type; r,x,y: T :: r = x + y} */ + public static final int ADD = 14; + + /** {@code T: any numeric type; r,x,y: T :: r = x - y} */ + public static final int SUB = 15; + + /** {@code T: any numeric type; r,x,y: T :: r = x * y} */ + public static final int MUL = 16; + + /** {@code T: any numeric type; r,x,y: T :: r = x / y} */ + public static final int DIV = 17; + + /** + * {@code T: any numeric type; r,x,y: T :: r = x % y} + * (Java-style remainder) + */ + public static final int REM = 18; + + /** {@code T: any numeric type; r,x: T :: r = -x} */ + public static final int NEG = 19; + + /** {@code T: any integral type; r,x,y: T :: r = x & y} */ + public static final int AND = 20; + + /** {@code T: any integral type; r,x,y: T :: r = x | y} */ + public static final int OR = 21; + + /** {@code T: any integral type; r,x,y: T :: r = x ^ y} */ + public static final int XOR = 22; + + /** + * {@code T: any integral type; r,x: T; y: int :: r = x << y} + */ + public static final int SHL = 23; + + /** + * {@code T: any integral type; r,x: T; y: int :: r = x >> y} + * (signed right-shift) + */ + public static final int SHR = 24; + + /** + * {@code T: any integral type; r,x: T; y: int :: r = x >>> y} + * (unsigned right-shift) + */ + public static final int USHR = 25; + + /** {@code T: any integral type; r,x: T :: r = ~x} */ + public static final int NOT = 26; + + /** + * {@code T: any numeric type; r: int; x,y: T :: r = (x == y) ? 0 + * : (x > y) ? 1 : -1} (Java-style "cmpl" where a NaN is + * considered "less than" all other values; also used for integral + * comparisons) + */ + public static final int CMPL = 27; + + /** + * {@code T: any floating point type; r: int; x,y: T :: r = (x == y) ? 0 + * : (x < y) ? -1 : 1} (Java-style "cmpg" where a NaN is + * considered "greater than" all other values) + */ + public static final int CMPG = 28; + + /** + * {@code T: any numeric type; U: any numeric type; r: T; x: U :: + * r = (T) x} (numeric type conversion between the four + * "real" numeric types) + */ + public static final int CONV = 29; + + /** + * {@code r,x: int :: r = (x << 24) >> 24} (Java-style + * convert int to byte) + */ + public static final int TO_BYTE = 30; + + /** + * {@code r,x: int :: r = x & 0xffff} (Java-style convert int to char) + */ + public static final int TO_CHAR = 31; + + /** + * {@code r,x: int :: r = (x << 16) >> 16} (Java-style + * convert int to short) + */ + public static final int TO_SHORT = 32; + + /** {@code T: return type for the method; x: T; return x} */ + public static final int RETURN = 33; + + /** {@code T: any type; r: int; x: T[]; :: r = x.length} */ + public static final int ARRAY_LENGTH = 34; + + /** {@code x: Throwable :: throw(x)} */ + public static final int THROW = 35; + + /** {@code x: Object :: monitorenter(x)} */ + public static final int MONITOR_ENTER = 36; + + /** {@code x: Object :: monitorexit(x)} */ + public static final int MONITOR_EXIT = 37; + + /** {@code T: any type; r: T; x: T[]; y: int :: r = x[y]} */ + public static final int AGET = 38; + + /** {@code T: any type; x: T; y: T[]; z: int :: x[y] = z} */ + public static final int APUT = 39; + + /** + * {@code T: any non-array object type :: r = + * alloc(T)} (allocate heap space for an object) + */ + public static final int NEW_INSTANCE = 40; + + /** {@code T: any array type; r: T; x: int :: r = new T[x]} */ + public static final int NEW_ARRAY = 41; + + /** + * {@code T: any array type; r: T; x: int; v0..vx: T :: r = new T[x] + * {v0, ..., vx}} + */ + public static final int FILLED_NEW_ARRAY = 42; + + /** + * {@code T: any object type; x: Object :: (T) x} (can + * throw {@code ClassCastException}) + */ + public static final int CHECK_CAST = 43; + + /** + * {@code T: any object type; x: Object :: x instanceof T} + */ + public static final int INSTANCE_OF = 44; + + /** + * {@code T: any type; r: T; x: Object; f: instance field spec of + * type T :: r = x.f} + */ + public static final int GET_FIELD = 45; + + /** + * {@code T: any type; r: T; f: static field spec of type T :: r = + * f} + */ + public static final int GET_STATIC = 46; + + /** + * {@code T: any type; x: T; y: Object; f: instance field spec of type + * T :: y.f = x} + */ + public static final int PUT_FIELD = 47; + + /** + * {@code T: any type; f: static field spec of type T; x: T :: f = x} + */ + public static final int PUT_STATIC = 48; + + /** + * {@code Tr, T0, T1...: any types; r: Tr; m: static method spec; + * y0: T0; y1: T1 ... :: r = m(y0, y1, ...)} (call static + * method) + */ + public static final int INVOKE_STATIC = 49; + + /** + * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method + * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call normal + * virtual method) + */ + public static final int INVOKE_VIRTUAL = 50; + + /** + * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method + * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call + * superclass virtual method) + */ + public static final int INVOKE_SUPER = 51; + + /** + * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method + * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call + * direct/special method) + */ + public static final int INVOKE_DIRECT = 52; + + /** + * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: interface + * (instance) method spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, + * ...)} (call interface method) + */ + public static final int INVOKE_INTERFACE = 53; + + /** + * {@code T0: any type; name: local variable name :: mark(name,T0)} + * (mark beginning or end of local variable name) + */ + public static final int MARK_LOCAL = 54; + + /** + * {@code T: Any type; r: T :: r = return_type}. + * <b>Note:</b> This opcode should only ever be used in the + * first instruction of a block following an invoke-*. + */ + public static final int MOVE_RESULT = 55; + + /** + * {@code T: Any type; r: T :: r = return_type}. + * <b>Note:</b> This opcode should only ever be used in the + * first instruction of a block following a non-invoke throwing insn + */ + public static final int MOVE_RESULT_PSEUDO = 56; + + /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */ + public static final int FILL_ARRAY_DATA = 57; + + /** + * This class is uninstantiable. + */ + private RegOps() { + // This space intentionally left blank. + } + + /** + * Gets the name of the given opcode. + * + * @param opcode the opcode + * @return {@code non-null;} its name + */ + public static String opName(int opcode) { + switch (opcode) { + case NOP: + return "nop"; + case MOVE: + return "move"; + case MOVE_PARAM: + return "move-param"; + case MOVE_EXCEPTION: + return "move-exception"; + case CONST: + return "const"; + case GOTO: + return "goto"; + case IF_EQ: + return "if-eq"; + case IF_NE: + return "if-ne"; + case IF_LT: + return "if-lt"; + case IF_GE: + return "if-ge"; + case IF_LE: + return "if-le"; + case IF_GT: + return "if-gt"; + case SWITCH: + return "switch"; + case ADD: + return "add"; + case SUB: + return "sub"; + case MUL: + return "mul"; + case DIV: + return "div"; + case REM: + return "rem"; + case NEG: + return "neg"; + case AND: + return "and"; + case OR: + return "or"; + case XOR: + return "xor"; + case SHL: + return "shl"; + case SHR: + return "shr"; + case USHR: + return "ushr"; + case NOT: + return "not"; + case CMPL: + return "cmpl"; + case CMPG: + return "cmpg"; + case CONV: + return "conv"; + case TO_BYTE: + return "to-byte"; + case TO_CHAR: + return "to-char"; + case TO_SHORT: + return "to-short"; + case RETURN: + return "return"; + case ARRAY_LENGTH: + return "array-length"; + case THROW: + return "throw"; + case MONITOR_ENTER: + return "monitor-enter"; + case MONITOR_EXIT: + return "monitor-exit"; + case AGET: + return "aget"; + case APUT: + return "aput"; + case NEW_INSTANCE: + return "new-instance"; + case NEW_ARRAY: + return "new-array"; + case FILLED_NEW_ARRAY: + return "filled-new-array"; + case CHECK_CAST: + return "check-cast"; + case INSTANCE_OF: + return "instance-of"; + case GET_FIELD: + return "get-field"; + case GET_STATIC: + return "get-static"; + case PUT_FIELD: + return "put-field"; + case PUT_STATIC: + return "put-static"; + case INVOKE_STATIC: + return "invoke-static"; + case INVOKE_VIRTUAL: + return "invoke-virtual"; + case INVOKE_SUPER: + return "invoke-super"; + case INVOKE_DIRECT: + return "invoke-direct"; + case INVOKE_INTERFACE: + return "invoke-interface"; + case MOVE_RESULT: + return "move-result"; + case MOVE_RESULT_PSEUDO: + return "move-result-pseudo"; + case FILL_ARRAY_DATA: + return "fill-array-data"; } - /** - * Gets the name of the given opcode. - * - * @param opcode the opcode - * @return {@code non-null;} its name - */ - public static String opName(int opcode) { - switch (opcode) { - case NOP: return "nop"; - case MOVE: return "move"; - case MOVE_PARAM: return "move-param"; - case MOVE_EXCEPTION: return "move-exception"; - case CONST: return "const"; - case GOTO: return "goto"; - case IF_EQ: return "if-eq"; - case IF_NE: return "if-ne"; - case IF_LT: return "if-lt"; - case IF_GE: return "if-ge"; - case IF_LE: return "if-le"; - case IF_GT: return "if-gt"; - case SWITCH: return "switch"; - case ADD: return "add"; - case SUB: return "sub"; - case MUL: return "mul"; - case DIV: return "div"; - case REM: return "rem"; - case NEG: return "neg"; - case AND: return "and"; - case OR: return "or"; - case XOR: return "xor"; - case SHL: return "shl"; - case SHR: return "shr"; - case USHR: return "ushr"; - case NOT: return "not"; - case CMPL: return "cmpl"; - case CMPG: return "cmpg"; - case CONV: return "conv"; - case TO_BYTE: return "to-byte"; - case TO_CHAR: return "to-char"; - case TO_SHORT: return "to-short"; - case RETURN: return "return"; - case ARRAY_LENGTH: return "array-length"; - case THROW: return "throw"; - case MONITOR_ENTER: return "monitor-enter"; - case MONITOR_EXIT: return "monitor-exit"; - case AGET: return "aget"; - case APUT: return "aput"; - case NEW_INSTANCE: return "new-instance"; - case NEW_ARRAY: return "new-array"; - case FILLED_NEW_ARRAY: return "filled-new-array"; - case CHECK_CAST: return "check-cast"; - case INSTANCE_OF: return "instance-of"; - case GET_FIELD: return "get-field"; - case GET_STATIC: return "get-static"; - case PUT_FIELD: return "put-field"; - case PUT_STATIC: return "put-static"; - case INVOKE_STATIC: return "invoke-static"; - case INVOKE_VIRTUAL: return "invoke-virtual"; - case INVOKE_SUPER: return "invoke-super"; - case INVOKE_DIRECT: return "invoke-direct"; - case INVOKE_INTERFACE: return "invoke-interface"; - case MOVE_RESULT: return "move-result"; - case MOVE_RESULT_PSEUDO: return "move-result-pseudo"; - case FILL_ARRAY_DATA: return "fill-array-data"; - } - - return "unknown-" + Hex.u1(opcode); - } - - /** - * Given an IF_* RegOp, returns the right-to-left flipped version. For - * example, IF_GT becomes IF_LT. - * - * @param opcode An IF_* RegOp - * @return flipped IF Regop - */ - public static int flippedIfOpcode(final int opcode) { - switch (opcode) { - case RegOps.IF_EQ: - case RegOps.IF_NE: - return opcode; - case RegOps.IF_LT: - return RegOps.IF_GT; - case RegOps.IF_GE: - return RegOps.IF_LE; - case RegOps.IF_LE: - return RegOps.IF_GE; - case RegOps.IF_GT: - return RegOps.IF_LT; - default: - throw new RuntimeException("Unrecognized IF regop: " + opcode); - } + return "unknown-" + Hex.u1(opcode); + } + + /** + * Given an IF_* RegOp, returns the right-to-left flipped version. For + * example, IF_GT becomes IF_LT. + * + * @param opcode An IF_* RegOp + * @return flipped IF Regop + */ + public static int flippedIfOpcode(final int opcode) { + switch (opcode) { + case RegOps.IF_EQ: + case RegOps.IF_NE: + return opcode; + case RegOps.IF_LT: + return RegOps.IF_GT; + case RegOps.IF_GE: + return RegOps.IF_LE; + case RegOps.IF_LE: + return RegOps.IF_GE; + case RegOps.IF_GT: + return RegOps.IF_LT; + default: + throw new RuntimeException("Unrecognized IF regop: " + opcode); } + } } diff --git a/dx/src/com/android/jack/dx/rop/code/RegisterSpec.java b/dx/src/com/android/jack/dx/rop/code/RegisterSpec.java index 2460041..e9f1474 100644 --- a/dx/src/com/android/jack/dx/rop/code/RegisterSpec.java +++ b/dx/src/com/android/jack/dx/rop/code/RegisterSpec.java @@ -28,630 +28,626 @@ import java.util.HashMap; * Combination of a register number and a type, used as the sources and * destinations of register-based operations. */ -public final class RegisterSpec - implements TypeBearer, ToHuman, Comparable<RegisterSpec> { - /** {@code non-null;} string to prefix register numbers with */ - public static final String PREFIX = "v"; +public final class RegisterSpec implements TypeBearer, ToHuman, Comparable<RegisterSpec> { + /** {@code non-null;} string to prefix register numbers with */ + public static final String PREFIX = "v"; + + /** {@code non-null;} intern table for instances */ + private static final HashMap<Object, RegisterSpec> theInterns = + new HashMap<Object, RegisterSpec>(1000); + + /** {@code non-null;} common comparison instance used while interning */ + private static final ForComparison theInterningItem = new ForComparison(); + + /** {@code >= 0;} register number */ + private final int reg; + + /** {@code non-null;} type loaded or stored */ + private final TypeBearer type; + + /** + * {@code null-ok;} local variable info associated with this register, + * if any + */ + private final LocalItem local; + + /** + * Intern the given triple as an instance of this class. + * + * @param reg {@code >= 0;} the register number + * @param type {@code non-null;} the type (or possibly actual value) which + * is loaded from or stored to the indicated register + * @param local {@code null-ok;} the associated local variable, if any + * @return {@code non-null;} an appropriately-constructed instance + */ + private static RegisterSpec intern(int reg, TypeBearer type, LocalItem local) { + synchronized (theInterns) { + theInterningItem.set(reg, type, local); + RegisterSpec found = theInterns.get(theInterningItem); + + if (found != null) { + return found; + } + + found = theInterningItem.toRegisterSpec(); + theInterns.put(found, found); + return found; + } + } + + /** + * Returns an instance for the given register number and type, with + * no variable info. This method is allowed to return shared + * instances (but doesn't necessarily do so). + * + * @param reg {@code >= 0;} the register number + * @param type {@code non-null;} the type (or possibly actual value) which + * is loaded from or stored to the indicated register + * @return {@code non-null;} an appropriately-constructed instance + */ + public static RegisterSpec make(int reg, TypeBearer type) { + return intern(reg, type, null); + } + + /** + * Returns an instance for the given register number, type, and + * variable info. This method is allowed to return shared + * instances (but doesn't necessarily do so). + * + * @param reg {@code >= 0;} the register number + * @param type {@code non-null;} the type (or possibly actual value) which + * is loaded from or stored to the indicated register + * @param local {@code non-null;} the associated local variable + * @return {@code non-null;} an appropriately-constructed instance + */ + public static RegisterSpec make(int reg, TypeBearer type, LocalItem local) { + if (local == null) { + throw new NullPointerException("local == null"); + } + + return intern(reg, type, local); + } + + /** + * Returns an instance for the given register number, type, and + * variable info. This method is allowed to return shared + * instances (but doesn't necessarily do so). + * + * @param reg {@code >= 0;} the register number + * @param type {@code non-null;} the type (or possibly actual value) which + * is loaded from or stored to the indicated register + * @param local {@code null-ok;} the associated variable info or null for + * none + * @return {@code non-null;} an appropriately-constructed instance + */ + public static RegisterSpec makeLocalOptional(int reg, TypeBearer type, LocalItem local) { + + return intern(reg, type, local); + } + + /** + * Gets the string form for the given register number. + * + * @param reg {@code >= 0;} the register number + * @return {@code non-null;} the string form + */ + public static String regString(int reg) { + return PREFIX + reg; + } + + /** + * Constructs an instance. This constructor is private. Use + * {@link #make}. + * + * @param reg {@code >= 0;} the register number + * @param type {@code non-null;} the type (or possibly actual value) which + * is loaded from or stored to the indicated register + * @param local {@code null-ok;} the associated local variable, if any + */ + private RegisterSpec(int reg, TypeBearer type, LocalItem local) { + if (reg < 0) { + throw new IllegalArgumentException("reg < 0"); + } + + if (type == null) { + throw new NullPointerException("type == null"); + } + + this.reg = reg; + this.type = type; + this.local = local; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof RegisterSpec)) { + if (other instanceof ForComparison) { + ForComparison fc = (ForComparison) other; + return equals(fc.reg, fc.type, fc.local); + } + return false; + } + + RegisterSpec spec = (RegisterSpec) other; + return equals(spec.reg, spec.type, spec.local); + } + + /** + * Like {@code equals}, but only consider the simple types of the + * registers. That is, this compares {@code getType()} on the types + * to ignore whatever arbitrary extra stuff might be carried around + * by an outer {@link TypeBearer}. + * + * @param other {@code null-ok;} spec to compare to + * @return {@code true} iff {@code this} and {@code other} are equal + * in the stated way + */ + public boolean equalsUsingSimpleType(RegisterSpec other) { + if (!matchesVariable(other)) { + return false; + } + + return (reg == other.reg); + } + + /** + * Like {@link #equalsUsingSimpleType} but ignoring the register number. + * This is useful to determine if two instances refer to the "same" + * local variable. + * + * @param other {@code null-ok;} spec to compare to + * @return {@code true} iff {@code this} and {@code other} are equal + * in the stated way + */ + public boolean matchesVariable(RegisterSpec other) { + if (other == null) { + return false; + } + + return type.getType().equals(other.type.getType()) + && ((local == other.local) || ((local != null) && local.equals(other.local))); + } + + /** + * Helper for {@link #equals} and {@link #ForComparison.equals}, + * which actually does the test. + * + * @param reg value of the instance variable, for another instance + * @param type value of the instance variable, for another instance + * @param local value of the instance variable, for another instance + * @return whether this instance is equal to one with the given + * values + */ + private boolean equals(int reg, TypeBearer type, LocalItem local) { + return (this.reg == reg) && this.type.equals(type) + && ((this.local == local) || ((this.local != null) && this.local.equals(local))); + } + + /** + * Compares by (in priority order) register number, unwrapped type + * (that is types not {@link TypeBearer}s, and local info. + * + * @param other {@code non-null;} spec to compare to + * @return {@code -1..1;} standard result of comparison + */ + @Override + public int compareTo(RegisterSpec other) { + if (this.reg < other.reg) { + return -1; + } else if (this.reg > other.reg) { + return 1; + } + + int compare = type.getType().compareTo(other.type.getType()); + + if (compare != 0) { + return compare; + } + + if (this.local == null) { + return (other.local == null) ? 0 : -1; + } else if (other.local == null) { + return 1; + } + + return this.local.compareTo(other.local); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return hashCodeOf(reg, type, local); + } + + /** + * Helper for {@link #hashCode} and {@link #ForComparison.hashCode}, + * which actually does the calculation. + * + * @param reg value of the instance variable + * @param type value of the instance variable + * @param local value of the instance variable + * @return the hash code + */ + private static int hashCodeOf(int reg, TypeBearer type, LocalItem local) { + int hash = (local != null) ? local.hashCode() : 0; + + hash = (hash * 31 + type.hashCode()) * 31 + reg; + return hash; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return toString0(false); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return toString0(true); + } + + /** {@inheritDoc} */ + @Override + public Type getType() { + return type.getType(); + } + + /** {@inheritDoc} */ + @Override + public TypeBearer getFrameType() { + return type.getFrameType(); + } + + /** {@inheritDoc} */ + @Override + public final int getBasicType() { + return type.getBasicType(); + } + + /** {@inheritDoc} */ + @Override + public final int getBasicFrameType() { + return type.getBasicFrameType(); + } + + /** {@inheritDoc} */ + @Override + public final boolean isConstant() { + return false; + } + + /** + * Gets the register number. + * + * @return {@code >= 0;} the register number + */ + public int getReg() { + return reg; + } + + /** + * Gets the type (or actual value) which is loaded from or stored + * to the register associated with this instance. + * + * @return {@code non-null;} the type + */ + public TypeBearer getTypeBearer() { + return type; + } + + /** + * Gets the variable info associated with this instance, if any. + * + * @return {@code null-ok;} the variable info, or {@code null} if this + * instance has none + */ + public LocalItem getLocalItem() { + return local; + } + + /** + * Gets the next available register number after the one in this + * instance. This is equal to the register number plus the width + * (category) of the type used. Among other things, this may also + * be used to determine the minimum required register count + * implied by this instance. + * + * @return {@code >= 0;} the required registers size + */ + public int getNextReg() { + return reg + getCategory(); + } + + /** + * Gets the category of this instance's type. This is just a convenient + * shorthand for {@code getType().getCategory()}. + * + * @see #isCategory1 + * @see #isCategory2 + * @return {@code 1..2;} the category of this instance's type + */ + public int getCategory() { + return type.getType().getCategory(); + } + + /** + * Gets whether this instance's type is category 1. This is just a + * convenient shorthand for {@code getType().isCategory1()}. + * + * @see #getCategory + * @see #isCategory2 + * @return whether or not this instance's type is of category 1 + */ + public boolean isCategory1() { + return type.getType().isCategory1(); + } + + /** + * Gets whether this instance's type is category 2. This is just a + * convenient shorthand for {@code getType().isCategory2()}. + * + * @see #getCategory + * @see #isCategory1 + * @return whether or not this instance's type is of category 2 + */ + public boolean isCategory2() { + return type.getType().isCategory2(); + } + + /** + * Gets the string form for just the register number of this instance. + * + * @return {@code non-null;} the register string form + */ + public String regString() { + return regString(reg); + } + + /** + * Returns an instance that is the intersection between this instance + * and the given one, if any. The intersection is defined as follows: + * + * <ul> + * <li>If {@code other} is {@code null}, then the result + * is {@code null}. + * <li>If the register numbers don't match, then the intersection + * is {@code null}. Otherwise, the register number of the + * intersection is the same as the one in the two instances.</li> + * <li>If the types returned by {@code getType()} are not + * {@code equals()}, then the intersection is null.</li> + * <li>If the type bearers returned by {@code getTypeBearer()} + * are {@code equals()}, then the intersection's type bearer + * is the one from this instance. Otherwise, the intersection's + * type bearer is the {@code getType()} of this instance.</li> + * <li>If the locals are {@code equals()}, then the local info + * of the intersection is the local info of this instance. Otherwise, + * the local info of the intersection is {@code null}.</li> + * </ul> + * + * @param other {@code null-ok;} instance to intersect with (or {@code null}) + * @param localPrimary whether local variables are primary to the + * intersection; if {@code true}, then the only non-null + * results occur when registers being intersected have equal local + * infos (or both have {@code null} local infos) + * @return {@code null-ok;} the intersection + */ + public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) { + if (this == other) { + // Easy out. + return this; + } + + if ((other == null) || (reg != other.getReg())) { + return null; + } + + LocalItem resultLocal = ((local == null) || !local.equals(other.getLocalItem())) ? null : local; + boolean sameName = (resultLocal == local); + + if (localPrimary && !sameName) { + return null; + } + + Type thisType = getType(); + Type otherType = other.getType(); + + // Note: Types are always interned. + if (thisType != otherType) { + return null; + } + + TypeBearer resultTypeBearer = type.equals(other.getTypeBearer()) ? type : thisType; + + if ((resultTypeBearer == type) && sameName) { + // It turns out that the intersection is "this" after all. + return this; + } + + return (resultLocal == null) ? make(reg, resultTypeBearer) + : make(reg, resultTypeBearer, resultLocal); + } + + /** + * Returns an instance that is identical to this one, except that the + * register number is replaced by the given one. + * + * @param newReg {@code >= 0;} the new register number + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpec withReg(int newReg) { + if (reg == newReg) { + return this; + } + + return makeLocalOptional(newReg, type, local); + } + + /** + * Returns an instance that is identical to this one, except that + * the type is replaced by the given one. + * + * @param newType {@code non-null;} the new type + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpec withType(TypeBearer newType) { + return makeLocalOptional(reg, newType, local); + } + + /** + * Returns an instance that is identical to this one, except that the + * register number is offset by the given amount. + * + * @param delta the amount to offset the register number by + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpec withOffset(int delta) { + if (delta == 0) { + return this; + } + + return withReg(reg + delta); + } + + /** + * Returns an instance that is identical to this one, except that + * the type bearer is replaced by the actual underlying type + * (thereby stripping off non-type information) with any + * initialization information stripped away as well. + * + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpec withSimpleType() { + TypeBearer orig = type; + Type newType; + + if (orig instanceof Type) { + newType = (Type) orig; + } else { + newType = orig.getType(); + } + + if (newType.isUninitialized()) { + newType = newType.getInitializedType(); + } - /** {@code non-null;} intern table for instances */ - private static final HashMap<Object, RegisterSpec> theInterns = - new HashMap<Object, RegisterSpec>(1000); + if (newType == orig) { + return this; + } + + return makeLocalOptional(reg, newType, local); + } - /** {@code non-null;} common comparison instance used while interning */ - private static final ForComparison theInterningItem = new ForComparison(); + /** + * Returns an instance that is identical to this one except that the + * local variable is as specified in the parameter. + * + * @param local {@code null-ok;} the local item or null for none + * @return an appropriate instance + */ + public RegisterSpec withLocalItem(LocalItem local) { + if ((this.local == local) || ((this.local != null) && this.local.equals(local))) { + return this; + } + + return makeLocalOptional(reg, type, local); + } + + + /** + * Helper for {@link #toString} and {@link #toHuman}. + * + * @param human whether to be human-oriented + * @return {@code non-null;} the string form + */ + private String toString0(boolean human) { + StringBuffer sb = new StringBuffer(40); + + sb.append(regString()); + sb.append(":"); + + if (local != null) { + sb.append(local.toString()); + } + + Type justType = type.getType(); + sb.append(justType); + + if (justType != type) { + sb.append("="); + if (human && (type instanceof CstString)) { + sb.append(((CstString) type).toQuoted()); + } else if (human && (type instanceof Constant)) { + sb.append(type.toHuman()); + } else { + sb.append(type); + } + } + + return sb.toString(); + } + + /** + * Holder of register spec data for the purposes of comparison (so that + * {@code RegisterSpec} itself can still keep {@code final} + * instance variables. + */ + private static class ForComparison { /** {@code >= 0;} register number */ - private final int reg; + private int reg; /** {@code non-null;} type loaded or stored */ - private final TypeBearer type; + private TypeBearer type; /** - * {@code null-ok;} local variable info associated with this register, - * if any + * {@code null-ok;} local variable associated with this + * register, if any */ - private final LocalItem local; + private LocalItem local; /** - * Intern the given triple as an instance of this class. + * Set all the instance variables. * * @param reg {@code >= 0;} the register number - * @param type {@code non-null;} the type (or possibly actual value) which - * is loaded from or stored to the indicated register + * @param type {@code non-null;} the type (or possibly actual + * value) which is loaded from or stored to the indicated + * register * @param local {@code null-ok;} the associated local variable, if any * @return {@code non-null;} an appropriately-constructed instance */ - private static RegisterSpec intern(int reg, TypeBearer type, - LocalItem local) { - synchronized (theInterns) { - theInterningItem.set(reg, type, local); - RegisterSpec found = theInterns.get(theInterningItem); - - if (found != null) { - return found; - } - - found = theInterningItem.toRegisterSpec(); - theInterns.put(found, found); - return found; - } + public void set(int reg, TypeBearer type, LocalItem local) { + this.reg = reg; + this.type = type; + this.local = local; } /** - * Returns an instance for the given register number and type, with - * no variable info. This method is allowed to return shared - * instances (but doesn't necessarily do so). + * Construct a {@code RegisterSpec} of this instance's + * contents. * - * @param reg {@code >= 0;} the register number - * @param type {@code non-null;} the type (or possibly actual value) which - * is loaded from or stored to the indicated register * @return {@code non-null;} an appropriately-constructed instance */ - public static RegisterSpec make(int reg, TypeBearer type) { - return intern(reg, type, null); - } - - /** - * Returns an instance for the given register number, type, and - * variable info. This method is allowed to return shared - * instances (but doesn't necessarily do so). - * - * @param reg {@code >= 0;} the register number - * @param type {@code non-null;} the type (or possibly actual value) which - * is loaded from or stored to the indicated register - * @param local {@code non-null;} the associated local variable - * @return {@code non-null;} an appropriately-constructed instance - */ - public static RegisterSpec make(int reg, TypeBearer type, - LocalItem local) { - if (local == null) { - throw new NullPointerException("local == null"); - } - - return intern(reg, type, local); - } - - /** - * Returns an instance for the given register number, type, and - * variable info. This method is allowed to return shared - * instances (but doesn't necessarily do so). - * - * @param reg {@code >= 0;} the register number - * @param type {@code non-null;} the type (or possibly actual value) which - * is loaded from or stored to the indicated register - * @param local {@code null-ok;} the associated variable info or null for - * none - * @return {@code non-null;} an appropriately-constructed instance - */ - public static RegisterSpec makeLocalOptional( - int reg, TypeBearer type, LocalItem local) { - - return intern(reg, type, local); - } - - /** - * Gets the string form for the given register number. - * - * @param reg {@code >= 0;} the register number - * @return {@code non-null;} the string form - */ - public static String regString(int reg) { - return PREFIX + reg; - } - - /** - * Constructs an instance. This constructor is private. Use - * {@link #make}. - * - * @param reg {@code >= 0;} the register number - * @param type {@code non-null;} the type (or possibly actual value) which - * is loaded from or stored to the indicated register - * @param local {@code null-ok;} the associated local variable, if any - */ - private RegisterSpec(int reg, TypeBearer type, LocalItem local) { - if (reg < 0) { - throw new IllegalArgumentException("reg < 0"); - } - - if (type == null) { - throw new NullPointerException("type == null"); - } - - this.reg = reg; - this.type = type; - this.local = local; + public RegisterSpec toRegisterSpec() { + return new RegisterSpec(reg, type, local); } /** {@inheritDoc} */ @Override public boolean equals(Object other) { - if (!(other instanceof RegisterSpec)) { - if (other instanceof ForComparison) { - ForComparison fc = (ForComparison) other; - return equals(fc.reg, fc.type, fc.local); - } - return false; - } - - RegisterSpec spec = (RegisterSpec) other; - return equals(spec.reg, spec.type, spec.local); - } - - /** - * Like {@code equals}, but only consider the simple types of the - * registers. That is, this compares {@code getType()} on the types - * to ignore whatever arbitrary extra stuff might be carried around - * by an outer {@link TypeBearer}. - * - * @param other {@code null-ok;} spec to compare to - * @return {@code true} iff {@code this} and {@code other} are equal - * in the stated way - */ - public boolean equalsUsingSimpleType(RegisterSpec other) { - if (!matchesVariable(other)) { - return false; - } - - return (reg == other.reg); - } - - /** - * Like {@link #equalsUsingSimpleType} but ignoring the register number. - * This is useful to determine if two instances refer to the "same" - * local variable. - * - * @param other {@code null-ok;} spec to compare to - * @return {@code true} iff {@code this} and {@code other} are equal - * in the stated way - */ - public boolean matchesVariable(RegisterSpec other) { - if (other == null) { - return false; - } - - return type.getType().equals(other.type.getType()) - && ((local == other.local) - || ((local != null) && local.equals(other.local))); - } - - /** - * Helper for {@link #equals} and {@link #ForComparison.equals}, - * which actually does the test. - * - * @param reg value of the instance variable, for another instance - * @param type value of the instance variable, for another instance - * @param local value of the instance variable, for another instance - * @return whether this instance is equal to one with the given - * values - */ - private boolean equals(int reg, TypeBearer type, LocalItem local) { - return (this.reg == reg) - && this.type.equals(type) - && ((this.local == local) - || ((this.local != null) && this.local.equals(local))); - } + if (!(other instanceof RegisterSpec)) { + return false; + } - /** - * Compares by (in priority order) register number, unwrapped type - * (that is types not {@link TypeBearer}s, and local info. - * - * @param other {@code non-null;} spec to compare to - * @return {@code -1..1;} standard result of comparison - */ - public int compareTo(RegisterSpec other) { - if (this.reg < other.reg) { - return -1; - } else if (this.reg > other.reg) { - return 1; - } - - int compare = type.getType().compareTo(other.type.getType()); - - if (compare != 0) { - return compare; - } - - if (this.local == null) { - return (other.local == null) ? 0 : -1; - } else if (other.local == null) { - return 1; - } - - return this.local.compareTo(other.local); + RegisterSpec spec = (RegisterSpec) other; + return spec.equals(reg, type, local); } /** {@inheritDoc} */ @Override public int hashCode() { - return hashCodeOf(reg, type, local); - } - - /** - * Helper for {@link #hashCode} and {@link #ForComparison.hashCode}, - * which actually does the calculation. - * - * @param reg value of the instance variable - * @param type value of the instance variable - * @param local value of the instance variable - * @return the hash code - */ - private static int hashCodeOf(int reg, TypeBearer type, LocalItem local) { - int hash = (local != null) ? local.hashCode() : 0; - - hash = (hash * 31 + type.hashCode()) * 31 + reg; - return hash; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return toString0(false); - } - - /** {@inheritDoc} */ - public String toHuman() { - return toString0(true); - } - - /** {@inheritDoc} */ - public Type getType() { - return type.getType(); - } - - /** {@inheritDoc} */ - public TypeBearer getFrameType() { - return type.getFrameType(); - } - - /** {@inheritDoc} */ - public final int getBasicType() { - return type.getBasicType(); - } - - /** {@inheritDoc} */ - public final int getBasicFrameType() { - return type.getBasicFrameType(); - } - - /** {@inheritDoc} */ - public final boolean isConstant() { - return false; - } - - /** - * Gets the register number. - * - * @return {@code >= 0;} the register number - */ - public int getReg() { - return reg; - } - - /** - * Gets the type (or actual value) which is loaded from or stored - * to the register associated with this instance. - * - * @return {@code non-null;} the type - */ - public TypeBearer getTypeBearer() { - return type; - } - - /** - * Gets the variable info associated with this instance, if any. - * - * @return {@code null-ok;} the variable info, or {@code null} if this - * instance has none - */ - public LocalItem getLocalItem() { - return local; - } - - /** - * Gets the next available register number after the one in this - * instance. This is equal to the register number plus the width - * (category) of the type used. Among other things, this may also - * be used to determine the minimum required register count - * implied by this instance. - * - * @return {@code >= 0;} the required registers size - */ - public int getNextReg() { - return reg + getCategory(); - } - - /** - * Gets the category of this instance's type. This is just a convenient - * shorthand for {@code getType().getCategory()}. - * - * @see #isCategory1 - * @see #isCategory2 - * @return {@code 1..2;} the category of this instance's type - */ - public int getCategory() { - return type.getType().getCategory(); - } - - /** - * Gets whether this instance's type is category 1. This is just a - * convenient shorthand for {@code getType().isCategory1()}. - * - * @see #getCategory - * @see #isCategory2 - * @return whether or not this instance's type is of category 1 - */ - public boolean isCategory1() { - return type.getType().isCategory1(); - } - - /** - * Gets whether this instance's type is category 2. This is just a - * convenient shorthand for {@code getType().isCategory2()}. - * - * @see #getCategory - * @see #isCategory1 - * @return whether or not this instance's type is of category 2 - */ - public boolean isCategory2() { - return type.getType().isCategory2(); - } - - /** - * Gets the string form for just the register number of this instance. - * - * @return {@code non-null;} the register string form - */ - public String regString() { - return regString(reg); - } - - /** - * Returns an instance that is the intersection between this instance - * and the given one, if any. The intersection is defined as follows: - * - * <ul> - * <li>If {@code other} is {@code null}, then the result - * is {@code null}. - * <li>If the register numbers don't match, then the intersection - * is {@code null}. Otherwise, the register number of the - * intersection is the same as the one in the two instances.</li> - * <li>If the types returned by {@code getType()} are not - * {@code equals()}, then the intersection is null.</li> - * <li>If the type bearers returned by {@code getTypeBearer()} - * are {@code equals()}, then the intersection's type bearer - * is the one from this instance. Otherwise, the intersection's - * type bearer is the {@code getType()} of this instance.</li> - * <li>If the locals are {@code equals()}, then the local info - * of the intersection is the local info of this instance. Otherwise, - * the local info of the intersection is {@code null}.</li> - * </ul> - * - * @param other {@code null-ok;} instance to intersect with (or {@code null}) - * @param localPrimary whether local variables are primary to the - * intersection; if {@code true}, then the only non-null - * results occur when registers being intersected have equal local - * infos (or both have {@code null} local infos) - * @return {@code null-ok;} the intersection - */ - public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) { - if (this == other) { - // Easy out. - return this; - } - - if ((other == null) || (reg != other.getReg())) { - return null; - } - - LocalItem resultLocal = - ((local == null) || !local.equals(other.getLocalItem())) - ? null : local; - boolean sameName = (resultLocal == local); - - if (localPrimary && !sameName) { - return null; - } - - Type thisType = getType(); - Type otherType = other.getType(); - - // Note: Types are always interned. - if (thisType != otherType) { - return null; - } - - TypeBearer resultTypeBearer = - type.equals(other.getTypeBearer()) ? type : thisType; - - if ((resultTypeBearer == type) && sameName) { - // It turns out that the intersection is "this" after all. - return this; - } - - return (resultLocal == null) ? make(reg, resultTypeBearer) : - make(reg, resultTypeBearer, resultLocal); - } - - /** - * Returns an instance that is identical to this one, except that the - * register number is replaced by the given one. - * - * @param newReg {@code >= 0;} the new register number - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpec withReg(int newReg) { - if (reg == newReg) { - return this; - } - - return makeLocalOptional(newReg, type, local); - } - - /** - * Returns an instance that is identical to this one, except that - * the type is replaced by the given one. - * - * @param newType {@code non-null;} the new type - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpec withType(TypeBearer newType) { - return makeLocalOptional(reg, newType, local); - } - - /** - * Returns an instance that is identical to this one, except that the - * register number is offset by the given amount. - * - * @param delta the amount to offset the register number by - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpec withOffset(int delta) { - if (delta == 0) { - return this; - } - - return withReg(reg + delta); - } - - /** - * Returns an instance that is identical to this one, except that - * the type bearer is replaced by the actual underlying type - * (thereby stripping off non-type information) with any - * initialization information stripped away as well. - * - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpec withSimpleType() { - TypeBearer orig = type; - Type newType; - - if (orig instanceof Type) { - newType = (Type) orig; - } else { - newType = orig.getType(); - } - - if (newType.isUninitialized()) { - newType = newType.getInitializedType(); - } - - if (newType == orig) { - return this; - } - - return makeLocalOptional(reg, newType, local); - } - - /** - * Returns an instance that is identical to this one except that the - * local variable is as specified in the parameter. - * - * @param local {@code null-ok;} the local item or null for none - * @return an appropriate instance - */ - public RegisterSpec withLocalItem(LocalItem local) { - if ((this.local== local) - || ((this.local != null) && this.local.equals(local))) { - - return this; - } - - return makeLocalOptional(reg, type, local); - } - - - /** - * Helper for {@link #toString} and {@link #toHuman}. - * - * @param human whether to be human-oriented - * @return {@code non-null;} the string form - */ - private String toString0(boolean human) { - StringBuffer sb = new StringBuffer(40); - - sb.append(regString()); - sb.append(":"); - - if (local != null) { - sb.append(local.toString()); - } - - Type justType = type.getType(); - sb.append(justType); - - if (justType != type) { - sb.append("="); - if (human && (type instanceof CstString)) { - sb.append(((CstString) type).toQuoted()); - } else if (human && (type instanceof Constant)) { - sb.append(type.toHuman()); - } else { - sb.append(type); - } - } - - return sb.toString(); - } - - /** - * Holder of register spec data for the purposes of comparison (so that - * {@code RegisterSpec} itself can still keep {@code final} - * instance variables. - */ - private static class ForComparison { - /** {@code >= 0;} register number */ - private int reg; - - /** {@code non-null;} type loaded or stored */ - private TypeBearer type; - - /** - * {@code null-ok;} local variable associated with this - * register, if any - */ - private LocalItem local; - - /** - * Set all the instance variables. - * - * @param reg {@code >= 0;} the register number - * @param type {@code non-null;} the type (or possibly actual - * value) which is loaded from or stored to the indicated - * register - * @param local {@code null-ok;} the associated local variable, if any - * @return {@code non-null;} an appropriately-constructed instance - */ - public void set(int reg, TypeBearer type, LocalItem local) { - this.reg = reg; - this.type = type; - this.local = local; - } - - /** - * Construct a {@code RegisterSpec} of this instance's - * contents. - * - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpec toRegisterSpec() { - return new RegisterSpec(reg, type, local); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof RegisterSpec)) { - return false; - } - - RegisterSpec spec = (RegisterSpec) other; - return spec.equals(reg, type, local); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return hashCodeOf(reg, type, local); - } + return hashCodeOf(reg, type, local); } + } } diff --git a/dx/src/com/android/jack/dx/rop/code/RegisterSpecList.java b/dx/src/com/android/jack/dx/rop/code/RegisterSpecList.java index 50649fe..9f270ba 100644 --- a/dx/src/com/android/jack/dx/rop/code/RegisterSpecList.java +++ b/dx/src/com/android/jack/dx/rop/code/RegisterSpecList.java @@ -25,385 +25,383 @@ import java.util.BitSet; /** * List of {@link RegisterSpec} instances. */ -public final class RegisterSpecList - extends FixedSizeList implements TypeList { - /** {@code non-null;} no-element instance */ - public static final RegisterSpecList EMPTY = new RegisterSpecList(0); - - /** - * Makes a single-element instance. - * - * @param spec {@code non-null;} the element - * @return {@code non-null;} an appropriately-constructed instance - */ - public static RegisterSpecList make(RegisterSpec spec) { - RegisterSpecList result = new RegisterSpecList(1); - result.set(0, spec); - return result; +public final class RegisterSpecList extends FixedSizeList implements TypeList { + /** {@code non-null;} no-element instance */ + public static final RegisterSpecList EMPTY = new RegisterSpecList(0); + + /** + * Makes a single-element instance. + * + * @param spec {@code non-null;} the element + * @return {@code non-null;} an appropriately-constructed instance + */ + public static RegisterSpecList make(RegisterSpec spec) { + RegisterSpecList result = new RegisterSpecList(1); + result.set(0, spec); + return result; + } + + /** + * Makes a two-element instance. + * + * @param spec0 {@code non-null;} the first element + * @param spec1 {@code non-null;} the second element + * @return {@code non-null;} an appropriately-constructed instance + */ + public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1) { + RegisterSpecList result = new RegisterSpecList(2); + result.set(0, spec0); + result.set(1, spec1); + return result; + } + + /** + * Makes a three-element instance. + * + * @param spec0 {@code non-null;} the first element + * @param spec1 {@code non-null;} the second element + * @param spec2 {@code non-null;} the third element + * @return {@code non-null;} an appropriately-constructed instance + */ + public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1, RegisterSpec spec2) { + RegisterSpecList result = new RegisterSpecList(3); + result.set(0, spec0); + result.set(1, spec1); + result.set(2, spec2); + return result; + } + + /** + * Makes a four-element instance. + * + * @param spec0 {@code non-null;} the first element + * @param spec1 {@code non-null;} the second element + * @param spec2 {@code non-null;} the third element + * @param spec3 {@code non-null;} the fourth element + * @return {@code non-null;} an appropriately-constructed instance + */ + public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1, RegisterSpec spec2, + RegisterSpec spec3) { + RegisterSpecList result = new RegisterSpecList(4); + result.set(0, spec0); + result.set(1, spec1); + result.set(2, spec2); + result.set(3, spec3); + return result; + } + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size the size of the list + */ + public RegisterSpecList(int size) { + super(size); + } + + /** {@inheritDoc} */ + @Override + public Type getType(int n) { + return get(n).getType().getType(); + } + + /** {@inheritDoc} */ + @Override + public int getWordCount() { + int sz = size(); + int result = 0; + + for (int i = 0; i < sz; i++) { + result += getType(i).getCategory(); } - /** - * Makes a two-element instance. - * - * @param spec0 {@code non-null;} the first element - * @param spec1 {@code non-null;} the second element - * @return {@code non-null;} an appropriately-constructed instance - */ - public static RegisterSpecList make(RegisterSpec spec0, - RegisterSpec spec1) { - RegisterSpecList result = new RegisterSpecList(2); - result.set(0, spec0); - result.set(1, spec1); - return result; + return result; + } + + /** {@inheritDoc} */ + @Override + public TypeList withAddedType(Type type) { + throw new UnsupportedOperationException("unsupported"); + } + + /** + * Gets the indicated element. It is an error to call this with the + * index for an element which was never set; if you do that, this + * will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element + */ + public RegisterSpec get(int n) { + return (RegisterSpec) get0(n); + } + + /** + * Returns a RegisterSpec in this list that uses the specified register, + * or null if there is none in this list. + * @param reg Register to find + * @return RegisterSpec that uses argument or null. + */ + public RegisterSpec specForRegister(int reg) { + int sz = size(); + for (int i = 0; i < sz; i++) { + RegisterSpec rs; + + rs = get(i); + + if (rs.getReg() == reg) { + return rs; + } } - /** - * Makes a three-element instance. - * - * @param spec0 {@code non-null;} the first element - * @param spec1 {@code non-null;} the second element - * @param spec2 {@code non-null;} the third element - * @return {@code non-null;} an appropriately-constructed instance - */ - public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1, - RegisterSpec spec2) { - RegisterSpecList result = new RegisterSpecList(3); - result.set(0, spec0); - result.set(1, spec1); - result.set(2, spec2); - return result; + return null; + } + + /** + * Returns the index of a RegisterSpec in this list that uses the specified + * register, or -1 if none in this list uses the register. + * @param reg Register to find + * @return index of RegisterSpec or -1 + */ + public int indexOfRegister(int reg) { + int sz = size(); + for (int i = 0; i < sz; i++) { + RegisterSpec rs; + + rs = get(i); + + if (rs.getReg() == reg) { + return i; + } } - /** - * Makes a four-element instance. - * - * @param spec0 {@code non-null;} the first element - * @param spec1 {@code non-null;} the second element - * @param spec2 {@code non-null;} the third element - * @param spec3 {@code non-null;} the fourth element - * @return {@code non-null;} an appropriately-constructed instance - */ - public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1, - RegisterSpec spec2, - RegisterSpec spec3) { - RegisterSpecList result = new RegisterSpecList(4); - result.set(0, spec0); - result.set(1, spec1); - result.set(2, spec2); - result.set(3, spec3); - return result; + return -1; + } + + /** + * Sets the element at the given index. + * + * @param n {@code >= 0, < size();} which element + * @param spec {@code non-null;} the value to store + */ + public void set(int n, RegisterSpec spec) { + set0(n, spec); + } + + /** + * Gets the minimum required register count implied by this + * instance. This is equal to the highest register number referred + * to plus the widest width (largest category) of the type used in + * that register. + * + * @return {@code >= 0;} the required registers size + */ + public int getRegistersSize() { + int sz = size(); + int result = 0; + + for (int i = 0; i < sz; i++) { + RegisterSpec spec = (RegisterSpec) get0(i); + if (spec != null) { + int min = spec.getNextReg(); + if (min > result) { + result = min; + } + } } - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size the size of the list - */ - public RegisterSpecList(int size) { - super(size); + return result; + } + + /** + * Returns a new instance, which is the same as this instance, + * except that it has an additional element prepended to the original. + * Mutability of the result is inherited from the original. + * + * @param spec {@code non-null;} the new first spec (to prepend) + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpecList withFirst(RegisterSpec spec) { + int sz = size(); + RegisterSpecList result = new RegisterSpecList(sz + 1); + + for (int i = 0; i < sz; i++) { + result.set0(i + 1, get0(i)); } - /** {@inheritDoc} */ - public Type getType(int n) { - return get(n).getType().getType(); + result.set0(0, spec); + if (isImmutable()) { + result.setImmutable(); } - /** {@inheritDoc} */ - public int getWordCount() { - int sz = size(); - int result = 0; + return result; + } + + /** + * Returns a new instance, which is the same as this instance, + * except that its first element is removed. Mutability of the + * result is inherited from the original. + * + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpecList withoutFirst() { + int newSize = size() - 1; + + if (newSize == 0) { + return EMPTY; + } - for (int i = 0; i < sz; i++) { - result += getType(i).getCategory(); - } + RegisterSpecList result = new RegisterSpecList(newSize); - return result; + for (int i = 0; i < newSize; i++) { + result.set0(i, get0(i + 1)); } - /** {@inheritDoc} */ - public TypeList withAddedType(Type type) { - throw new UnsupportedOperationException("unsupported"); + if (isImmutable()) { + result.setImmutable(); } - /** - * Gets the indicated element. It is an error to call this with the - * index for an element which was never set; if you do that, this - * will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which element - * @return {@code non-null;} the indicated element - */ - public RegisterSpec get(int n) { - return (RegisterSpec) get0(n); + return result; + } + + /** + * Returns a new instance, which is the same as this instance, + * except that its last element is removed. Mutability of the + * result is inherited from the original. + * + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpecList withoutLast() { + int newSize = size() - 1; + + if (newSize == 0) { + return EMPTY; } - /** - * Returns a RegisterSpec in this list that uses the specified register, - * or null if there is none in this list. - * @param reg Register to find - * @return RegisterSpec that uses argument or null. - */ - public RegisterSpec specForRegister(int reg) { - int sz = size(); - for (int i = 0; i < sz; i++) { - RegisterSpec rs; - - rs = get(i); - - if (rs.getReg() == reg) { - return rs; - } - } + RegisterSpecList result = new RegisterSpecList(newSize); - return null; + for (int i = 0; i < newSize; i++) { + result.set0(i, get0(i)); } - /** - * Returns the index of a RegisterSpec in this list that uses the specified - * register, or -1 if none in this list uses the register. - * @param reg Register to find - * @return index of RegisterSpec or -1 - */ - public int indexOfRegister(int reg) { - int sz = size(); - for (int i = 0; i < sz; i++) { - RegisterSpec rs; - - rs = get(i); - - if (rs.getReg() == reg) { - return i; - } - } - - return -1; + if (isImmutable()) { + result.setImmutable(); } - /** - * Sets the element at the given index. - * - * @param n {@code >= 0, < size();} which element - * @param spec {@code non-null;} the value to store - */ - public void set(int n, RegisterSpec spec) { - set0(n, spec); + return result; + } + + /** + * Returns a new instance, which contains a subset of the elements + * specified by the given BitSet. Indexes in the BitSet with a zero + * are included, while indexes with a one are excluded. Mutability + * of the result is inherited from the original. + * + * @param exclusionSet {@code non-null;} set of registers to exclude + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpecList subset(BitSet exclusionSet) { + int newSize = size() - exclusionSet.cardinality(); + + if (newSize == 0) { + return EMPTY; } - /** - * Gets the minimum required register count implied by this - * instance. This is equal to the highest register number referred - * to plus the widest width (largest category) of the type used in - * that register. - * - * @return {@code >= 0;} the required registers size - */ - public int getRegistersSize() { - int sz = size(); - int result = 0; - - for (int i = 0; i < sz; i++) { - RegisterSpec spec = (RegisterSpec) get0(i); - if (spec != null) { - int min = spec.getNextReg(); - if (min > result) { - result = min; - } - } - } + RegisterSpecList result = new RegisterSpecList(newSize); - return result; + int newIndex = 0; + for (int oldIndex = 0; oldIndex < size(); oldIndex++) { + if (!exclusionSet.get(oldIndex)) { + result.set0(newIndex, get0(oldIndex)); + newIndex++; + } } - /** - * Returns a new instance, which is the same as this instance, - * except that it has an additional element prepended to the original. - * Mutability of the result is inherited from the original. - * - * @param spec {@code non-null;} the new first spec (to prepend) - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpecList withFirst(RegisterSpec spec) { - int sz = size(); - RegisterSpecList result = new RegisterSpecList(sz + 1); - - for (int i = 0; i < sz; i++) { - result.set0(i + 1, get0(i)); - } - - result.set0(0, spec); - if (isImmutable()) { - result.setImmutable(); - } - - return result; + if (isImmutable()) { + result.setImmutable(); } - /** - * Returns a new instance, which is the same as this instance, - * except that its first element is removed. Mutability of the - * result is inherited from the original. - * - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpecList withoutFirst() { - int newSize = size() - 1; - - if (newSize == 0) { - return EMPTY; - } - - RegisterSpecList result = new RegisterSpecList(newSize); - - for (int i = 0; i < newSize; i++) { - result.set0(i, get0(i + 1)); - } - - if (isImmutable()) { - result.setImmutable(); - } - - return result; + return result; + } + + /** + * Returns an instance that is identical to this one, except that + * all register numbers are offset by the given amount. Mutability + * of the result is inherited from the original. + * + * @param delta the amount to offset the register numbers by + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpecList withOffset(int delta) { + int sz = size(); + + if (sz == 0) { + // Don't bother making a new zero-element instance. + return this; } - /** - * Returns a new instance, which is the same as this instance, - * except that its last element is removed. Mutability of the - * result is inherited from the original. - * - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpecList withoutLast() { - int newSize = size() - 1; - - if (newSize == 0) { - return EMPTY; - } - - RegisterSpecList result = new RegisterSpecList(newSize); - - for (int i = 0; i < newSize; i++) { - result.set0(i, get0(i)); - } - - if (isImmutable()) { - result.setImmutable(); - } + RegisterSpecList result = new RegisterSpecList(sz); - return result; + for (int i = 0; i < sz; i++) { + RegisterSpec one = (RegisterSpec) get0(i); + if (one != null) { + result.set0(i, one.withOffset(delta)); + } } - /** - * Returns a new instance, which contains a subset of the elements - * specified by the given BitSet. Indexes in the BitSet with a zero - * are included, while indexes with a one are excluded. Mutability - * of the result is inherited from the original. - * - * @param exclusionSet {@code non-null;} set of registers to exclude - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpecList subset(BitSet exclusionSet) { - int newSize = size() - exclusionSet.cardinality(); - - if (newSize == 0) { - return EMPTY; - } - - RegisterSpecList result = new RegisterSpecList(newSize); - - int newIndex = 0; - for (int oldIndex = 0; oldIndex < size(); oldIndex++) { - if (!exclusionSet.get(oldIndex)) { - result.set0(newIndex, get0(oldIndex)); - newIndex++; - } - } - - if (isImmutable()) { - result.setImmutable(); - } - - return result; + if (isImmutable()) { + result.setImmutable(); } - /** - * Returns an instance that is identical to this one, except that - * all register numbers are offset by the given amount. Mutability - * of the result is inherited from the original. - * - * @param delta the amount to offset the register numbers by - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpecList withOffset(int delta) { - int sz = size(); - - if (sz == 0) { - // Don't bother making a new zero-element instance. - return this; - } - - RegisterSpecList result = new RegisterSpecList(sz); - - for (int i = 0; i < sz; i++) { - RegisterSpec one = (RegisterSpec) get0(i); - if (one != null) { - result.set0(i, one.withOffset(delta)); - } - } - - if (isImmutable()) { - result.setImmutable(); - } - - return result; + return result; + } + + /** + * Returns an instance that is identical to this one, except that + * all incompatible register numbers are renumbered sequentially from + * the given base, with the first number duplicated if indicated. If + * a null BitSet is given, it indicates all registers are compatible. + * + * @param base the base register number + * @param duplicateFirst whether to duplicate the first number + * @param compatRegs {@code null-ok;} either a {@code non-null} set of + * compatible registers, or {@code null} to indicate all registers are + * compatible + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpecList withExpandedRegisters(int base, boolean duplicateFirst, + BitSet compatRegs) { + int sz = size(); + + if (sz == 0) { + // Don't bother making a new zero-element instance. + return this; } - /** - * Returns an instance that is identical to this one, except that - * all incompatible register numbers are renumbered sequentially from - * the given base, with the first number duplicated if indicated. If - * a null BitSet is given, it indicates all registers are compatible. - * - * @param base the base register number - * @param duplicateFirst whether to duplicate the first number - * @param compatRegs {@code null-ok;} either a {@code non-null} set of - * compatible registers, or {@code null} to indicate all registers are - * compatible - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpecList withExpandedRegisters(int base, - boolean duplicateFirst, - BitSet compatRegs) { - int sz = size(); - - if (sz == 0) { - // Don't bother making a new zero-element instance. - return this; - } + RegisterSpecList result = new RegisterSpecList(sz); - RegisterSpecList result = new RegisterSpecList(sz); + for (int i = 0; i < sz; i++) { + RegisterSpec one = (RegisterSpec) get0(i); + boolean replace = (compatRegs == null) ? true : !compatRegs.get(i); - for (int i = 0; i < sz; i++) { - RegisterSpec one = (RegisterSpec) get0(i); - boolean replace = (compatRegs == null) ? true : !compatRegs.get(i); - - if (replace) { - result.set0(i, one.withReg(base)); - if (!duplicateFirst) { - base += one.getCategory(); - } - } else { - result.set0(i, one); - } - - if (duplicateFirst) { - duplicateFirst = false; - } + if (replace) { + result.set0(i, one.withReg(base)); + if (!duplicateFirst) { + base += one.getCategory(); } + } else { + result.set0(i, one); + } - if (isImmutable()) { - result.setImmutable(); - } + if (duplicateFirst) { + duplicateFirst = false; + } + } - return result; + if (isImmutable()) { + result.setImmutable(); } + + return result; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/RegisterSpecSet.java b/dx/src/com/android/jack/dx/rop/code/RegisterSpecSet.java index 4116155..7538027 100644 --- a/dx/src/com/android/jack/dx/rop/code/RegisterSpecSet.java +++ b/dx/src/com/android/jack/dx/rop/code/RegisterSpecSet.java @@ -22,375 +22,373 @@ import com.android.jack.dx.util.MutabilityControl; * Set of {@link RegisterSpec} instances, where a given register number * may appear only once in the set. */ -public final class RegisterSpecSet - extends MutabilityControl { - /** {@code non-null;} no-element instance */ - public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0); - - /** - * {@code non-null;} array of register specs, where each element is - * {@code null} or is an instance whose {@code reg} - * matches the array index - */ - private final RegisterSpec[] specs; - - /** {@code >= -1;} size of the set or {@code -1} if not yet calculated */ - private int size; - - /** - * Constructs an instance. The instance is initially empty. - * - * @param maxSize {@code >= 0;} the maximum register number (exclusive) that - * may be represented in this instance - */ - public RegisterSpecSet(int maxSize) { - super(maxSize != 0); - - this.specs = new RegisterSpec[maxSize]; - this.size = 0; +public final class RegisterSpecSet extends MutabilityControl { + /** {@code non-null;} no-element instance */ + public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0); + + /** + * {@code non-null;} array of register specs, where each element is + * {@code null} or is an instance whose {@code reg} + * matches the array index + */ + private final RegisterSpec[] specs; + + /** {@code >= -1;} size of the set or {@code -1} if not yet calculated */ + private int size; + + /** + * Constructs an instance. The instance is initially empty. + * + * @param maxSize {@code >= 0;} the maximum register number (exclusive) that + * may be represented in this instance + */ + public RegisterSpecSet(int maxSize) { + super(maxSize != 0); + + this.specs = new RegisterSpec[maxSize]; + this.size = 0; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof RegisterSpecSet)) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof RegisterSpecSet)) { - return false; - } - - RegisterSpecSet otherSet = (RegisterSpecSet) other; - RegisterSpec[] otherSpecs = otherSet.specs; - int len = specs.length; + RegisterSpecSet otherSet = (RegisterSpecSet) other; + RegisterSpec[] otherSpecs = otherSet.specs; + int len = specs.length; - if ((len != otherSpecs.length) || (size() != otherSet.size())) { - return false; - } + if ((len != otherSpecs.length) || (size() != otherSet.size())) { + return false; + } - for (int i = 0; i < len; i++) { - RegisterSpec s1 = specs[i]; - RegisterSpec s2 = otherSpecs[i]; + for (int i = 0; i < len; i++) { + RegisterSpec s1 = specs[i]; + RegisterSpec s2 = otherSpecs[i]; - if (s1 == s2) { - continue; - } + if (s1 == s2) { + continue; + } - if ((s1 == null) || !s1.equals(s2)) { - return false; - } - } - - return true; + if ((s1 == null) || !s1.equals(s2)) { + return false; + } } - /** {@inheritDoc} */ - @Override - public int hashCode() { - int len = specs.length; - int hash = 0; + return true; + } - for (int i = 0; i < len; i++) { - RegisterSpec spec = specs[i]; - int oneHash = (spec == null) ? 0 : spec.hashCode(); - hash = (hash * 31) + oneHash; - } + /** {@inheritDoc} */ + @Override + public int hashCode() { + int len = specs.length; + int hash = 0; - return hash; + for (int i = 0; i < len; i++) { + RegisterSpec spec = specs[i]; + int oneHash = (spec == null) ? 0 : spec.hashCode(); + hash = (hash * 31) + oneHash; } - /** {@inheritDoc} */ - @Override - public String toString() { - int len = specs.length; - StringBuffer sb = new StringBuffer(len * 25); - - sb.append('{'); - - boolean any = false; - for (int i = 0; i < len; i++) { - RegisterSpec spec = specs[i]; - if (spec != null) { - if (any) { - sb.append(", "); - } else { - any = true; - } - sb.append(spec); - } + return hash; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + int len = specs.length; + StringBuffer sb = new StringBuffer(len * 25); + + sb.append('{'); + + boolean any = false; + for (int i = 0; i < len; i++) { + RegisterSpec spec = specs[i]; + if (spec != null) { + if (any) { + sb.append(", "); + } else { + any = true; } - - sb.append('}'); - return sb.toString(); + sb.append(spec); + } } - /** - * Gets the maximum number of registers that may be in this instance, which - * is also the maximum-plus-one of register numbers that may be - * represented. - * - * @return {@code >= 0;} the maximum size - */ - public int getMaxSize() { - return specs.length; - } - - /** - * Gets the current size of this instance. - * - * @return {@code >= 0;} the size - */ - public int size() { - int result = size; - - if (result < 0) { - int len = specs.length; - - result = 0; - for (int i = 0; i < len; i++) { - if (specs[i] != null) { - result++; - } - } - - size = result; + sb.append('}'); + return sb.toString(); + } + + /** + * Gets the maximum number of registers that may be in this instance, which + * is also the maximum-plus-one of register numbers that may be + * represented. + * + * @return {@code >= 0;} the maximum size + */ + public int getMaxSize() { + return specs.length; + } + + /** + * Gets the current size of this instance. + * + * @return {@code >= 0;} the size + */ + public int size() { + int result = size; + + if (result < 0) { + int len = specs.length; + + result = 0; + for (int i = 0; i < len; i++) { + if (specs[i] != null) { + result++; } + } - return result; + size = result; } - /** - * Gets the element with the given register number, if any. - * - * @param reg {@code >= 0;} the desired register number - * @return {@code null-ok;} the element with the given register number or - * {@code null} if there is none - */ - public RegisterSpec get(int reg) { - try { - return specs[reg]; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("bogus reg"); - } + return result; + } + + /** + * Gets the element with the given register number, if any. + * + * @param reg {@code >= 0;} the desired register number + * @return {@code null-ok;} the element with the given register number or + * {@code null} if there is none + */ + public RegisterSpec get(int reg) { + try { + return specs[reg]; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("bogus reg"); } - - /** - * Gets the element with the same register number as the given - * spec, if any. This is just a convenient shorthand for - * {@code get(spec.getReg())}. - * - * @param spec {@code non-null;} spec with the desired register number - * @return {@code null-ok;} the element with the matching register number or - * {@code null} if there is none - */ - public RegisterSpec get(RegisterSpec spec) { - return get(spec.getReg()); + } + + /** + * Gets the element with the same register number as the given + * spec, if any. This is just a convenient shorthand for + * {@code get(spec.getReg())}. + * + * @param spec {@code non-null;} spec with the desired register number + * @return {@code null-ok;} the element with the matching register number or + * {@code null} if there is none + */ + public RegisterSpec get(RegisterSpec spec) { + return get(spec.getReg()); + } + + /** + * Returns the spec in this set that's currently associated with a + * given local (type, name, and signature), or {@code null} if there is + * none. This ignores the register number of the given spec but + * matches on everything else. + * + * @param spec {@code non-null;} local to look for + * @return {@code null-ok;} first register found that matches, if any + */ + public RegisterSpec findMatchingLocal(RegisterSpec spec) { + int length = specs.length; + + for (int reg = 0; reg < length; reg++) { + RegisterSpec s = specs[reg]; + + if (s == null) { + continue; + } + + if (spec.matchesVariable(s)) { + return s; + } } - /** - * Returns the spec in this set that's currently associated with a - * given local (type, name, and signature), or {@code null} if there is - * none. This ignores the register number of the given spec but - * matches on everything else. - * - * @param spec {@code non-null;} local to look for - * @return {@code null-ok;} first register found that matches, if any - */ - public RegisterSpec findMatchingLocal(RegisterSpec spec) { - int length = specs.length; - - for (int reg = 0; reg < length; reg++) { - RegisterSpec s = specs[reg]; - - if (s == null) { - continue; - } - - if (spec.matchesVariable(s)) { - return s; - } - } - - return null; + return null; + } + + /** + * Returns the spec in this set that's currently associated with a given + * local (name and signature), or {@code null} if there is none. + * + * @param local {@code non-null;} local item to search for + * @return {@code null-ok;} first register found with matching name and signature + */ + public RegisterSpec localItemToSpec(LocalItem local) { + int length = specs.length; + + for (int reg = 0; reg < length; reg++) { + RegisterSpec spec = specs[reg]; + + if ((spec != null) && local.equals(spec.getLocalItem())) { + return spec; + } } - /** - * Returns the spec in this set that's currently associated with a given - * local (name and signature), or {@code null} if there is none. - * - * @param local {@code non-null;} local item to search for - * @return {@code null-ok;} first register found with matching name and signature - */ - public RegisterSpec localItemToSpec(LocalItem local) { - int length = specs.length; - - for (int reg = 0; reg < length; reg++) { - RegisterSpec spec = specs[reg]; - - if ((spec != null) && local.equals(spec.getLocalItem())) { - return spec; - } - } - - return null; + return null; + } + + /** + * Removes a spec from the set. Only the register number + * of the parameter is significant. + * + * @param toRemove {@code non-null;} register to remove. + */ + public void remove(RegisterSpec toRemove) { + try { + specs[toRemove.getReg()] = null; + size = -1; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("bogus reg"); } - - /** - * Removes a spec from the set. Only the register number - * of the parameter is significant. - * - * @param toRemove {@code non-null;} register to remove. - */ - public void remove(RegisterSpec toRemove) { - try { - specs[toRemove.getReg()] = null; - size = -1; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("bogus reg"); - } + } + + /** + * Puts the given spec into the set. If there is already an element in + * the set with the same register number, it is replaced. Additionally, + * if the previous element is for a category-2 register, then that + * previous element is nullified. Finally, if the given spec is for + * a category-2 register, then the immediately subsequent element + * is nullified. + * + * @param spec {@code non-null;} the register spec to put in the instance + */ + public void put(RegisterSpec spec) { + throwIfImmutable(); + + if (spec == null) { + throw new NullPointerException("spec == null"); } - /** - * Puts the given spec into the set. If there is already an element in - * the set with the same register number, it is replaced. Additionally, - * if the previous element is for a category-2 register, then that - * previous element is nullified. Finally, if the given spec is for - * a category-2 register, then the immediately subsequent element - * is nullified. - * - * @param spec {@code non-null;} the register spec to put in the instance - */ - public void put(RegisterSpec spec) { - throwIfImmutable(); - - if (spec == null) { - throw new NullPointerException("spec == null"); - } + size = -1; - size = -1; - - try { - int reg = spec.getReg(); - specs[reg] = spec; - - if (reg > 0) { - int prevReg = reg - 1; - RegisterSpec prevSpec = specs[prevReg]; - if ((prevSpec != null) && (prevSpec.getCategory() == 2)) { - specs[prevReg] = null; - } - } - - if (spec.getCategory() == 2) { - specs[reg + 1] = null; - } - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("spec.getReg() out of range"); - } - } + try { + int reg = spec.getReg(); + specs[reg] = spec; - /** - * Put the entire contents of the given set into this one. - * - * @param set {@code non-null;} the set to put into this instance - */ - public void putAll(RegisterSpecSet set) { - int max = set.getMaxSize(); - - for (int i = 0; i < max; i++) { - RegisterSpec spec = set.get(i); - if (spec != null) { - put(spec); - } + if (reg > 0) { + int prevReg = reg - 1; + RegisterSpec prevSpec = specs[prevReg]; + if ((prevSpec != null) && (prevSpec.getCategory() == 2)) { + specs[prevReg] = null; } + } + + if (spec.getCategory() == 2) { + specs[reg + 1] = null; + } + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("spec.getReg() out of range"); } - - /** - * Intersects this instance with the given one, modifying this - * instance. The intersection consists of the pairwise - * {@link RegisterSpec#intersect} of corresponding elements from - * this instance and the given one where both are non-null. - * - * @param other {@code non-null;} set to intersect with - * @param localPrimary whether local variables are primary to - * the intersection; if {@code true}, then the only non-null - * result elements occur when registers being intersected have - * equal names (or both have {@code null} names) - */ - public void intersect(RegisterSpecSet other, boolean localPrimary) { - throwIfImmutable(); - - RegisterSpec[] otherSpecs = other.specs; - int thisLen = specs.length; - int len = Math.min(thisLen, otherSpecs.length); - - size = -1; - - for (int i = 0; i < len; i++) { - RegisterSpec spec = specs[i]; - - if (spec == null) { - continue; - } - - RegisterSpec intersection = - spec.intersect(otherSpecs[i], localPrimary); - if (intersection != spec) { - specs[i] = intersection; - } - } - - for (int i = len; i < thisLen; i++) { - specs[i] = null; - } + } + + /** + * Put the entire contents of the given set into this one. + * + * @param set {@code non-null;} the set to put into this instance + */ + public void putAll(RegisterSpecSet set) { + int max = set.getMaxSize(); + + for (int i = 0; i < max; i++) { + RegisterSpec spec = set.get(i); + if (spec != null) { + put(spec); + } + } + } + + /** + * Intersects this instance with the given one, modifying this + * instance. The intersection consists of the pairwise + * {@link RegisterSpec#intersect} of corresponding elements from + * this instance and the given one where both are non-null. + * + * @param other {@code non-null;} set to intersect with + * @param localPrimary whether local variables are primary to + * the intersection; if {@code true}, then the only non-null + * result elements occur when registers being intersected have + * equal names (or both have {@code null} names) + */ + public void intersect(RegisterSpecSet other, boolean localPrimary) { + throwIfImmutable(); + + RegisterSpec[] otherSpecs = other.specs; + int thisLen = specs.length; + int len = Math.min(thisLen, otherSpecs.length); + + size = -1; + + for (int i = 0; i < len; i++) { + RegisterSpec spec = specs[i]; + + if (spec == null) { + continue; + } + + RegisterSpec intersection = spec.intersect(otherSpecs[i], localPrimary); + if (intersection != spec) { + specs[i] = intersection; + } } - /** - * Returns an instance that is identical to this one, except that - * all register numbers are offset by the given amount. Mutability - * of the result is inherited from the original. - * - * @param delta the amount to offset the register numbers by - * @return {@code non-null;} an appropriately-constructed instance - */ - public RegisterSpecSet withOffset(int delta) { - int len = specs.length; - RegisterSpecSet result = new RegisterSpecSet(len + delta); - - for (int i = 0; i < len; i++) { - RegisterSpec spec = specs[i]; - if (spec != null) { - result.put(spec.withOffset(delta)); - } - } - - result.size = size; + for (int i = len; i < thisLen; i++) { + specs[i] = null; + } + } + + /** + * Returns an instance that is identical to this one, except that + * all register numbers are offset by the given amount. Mutability + * of the result is inherited from the original. + * + * @param delta the amount to offset the register numbers by + * @return {@code non-null;} an appropriately-constructed instance + */ + public RegisterSpecSet withOffset(int delta) { + int len = specs.length; + RegisterSpecSet result = new RegisterSpecSet(len + delta); + + for (int i = 0; i < len; i++) { + RegisterSpec spec = specs[i]; + if (spec != null) { + result.put(spec.withOffset(delta)); + } + } - if (isImmutable()) { - result.setImmutable(); - } + result.size = size; - return result; + if (isImmutable()) { + result.setImmutable(); } - /** - * Makes and return a mutable copy of this instance. - * - * @return {@code non-null;} the mutable copy - */ - public RegisterSpecSet mutableCopy() { - int len = specs.length; - RegisterSpecSet copy = new RegisterSpecSet(len); - - for (int i = 0; i < len; i++) { - RegisterSpec spec = specs[i]; - if (spec != null) { - copy.put(spec); - } - } + return result; + } + + /** + * Makes and return a mutable copy of this instance. + * + * @return {@code non-null;} the mutable copy + */ + public RegisterSpecSet mutableCopy() { + int len = specs.length; + RegisterSpecSet copy = new RegisterSpecSet(len); + + for (int i = 0; i < len; i++) { + RegisterSpec spec = specs[i]; + if (spec != null) { + copy.put(spec); + } + } - copy.size = size; + copy.size = size; - return copy; - } + return copy; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/Rop.java b/dx/src/com/android/jack/dx/rop/code/Rop.java index 161a0ee..3ae002b 100644 --- a/dx/src/com/android/jack/dx/rop/code/Rop.java +++ b/dx/src/com/android/jack/dx/rop/code/Rop.java @@ -25,383 +25,392 @@ import com.android.jack.dx.util.Hex; * Class that describes all the immutable parts of register-based operations. */ public final class Rop { - /** minimum {@code BRANCH_*} value */ - public static final int BRANCH_MIN = 1; - - /** indicates a non-branching op */ - public static final int BRANCH_NONE = 1; - - /** indicates a function/method return */ - public static final int BRANCH_RETURN = 2; - - /** indicates an unconditional goto */ - public static final int BRANCH_GOTO = 3; - - /** indicates a two-way branch */ - public static final int BRANCH_IF = 4; - - /** indicates a switch-style branch */ - public static final int BRANCH_SWITCH = 5; - - /** indicates a throw-style branch (both always-throws and may-throw) */ - public static final int BRANCH_THROW = 6; - - /** maximum {@code BRANCH_*} value */ - public static final int BRANCH_MAX = 6; - - /** the opcode; one of the constants in {@link RegOps} */ - private final int opcode; - - /** - * {@code non-null;} result type of this operation; {@link Type#VOID} for - * no-result operations - */ - private final Type result; - - /** {@code non-null;} types of all the sources of this operation */ - private final TypeList sources; - - /** {@code non-null;} list of possible types thrown by this operation */ - private final TypeList exceptions; - - /** - * the branchingness of this op; one of the {@code BRANCH_*} - * constants in this class - */ - private final int branchingness; - - /** whether this is a function/method call op or similar */ - private final boolean isCallLike; - - /** {@code null-ok;} nickname, if specified (used for debugging) */ - private final String nickname; - - /** - * Constructs an instance. This method is private. Use one of the - * public constructors. - * - * @param opcode the opcode; one of the constants in {@link RegOps} - * @param result {@code non-null;} result type of this operation; {@link - * Type#VOID} for no-result operations - * @param sources {@code non-null;} types of all the sources of this operation - * @param exceptions {@code non-null;} list of possible types thrown by this - * operation - * @param branchingness the branchingness of this op; one of the - * {@code BRANCH_*} constants - * @param isCallLike whether the op is a function/method call or similar - * @param nickname {@code null-ok;} optional nickname (used for debugging) - */ - public Rop(int opcode, Type result, TypeList sources, - TypeList exceptions, int branchingness, boolean isCallLike, - String nickname) { - if (result == null) { - throw new NullPointerException("result == null"); - } - - if (sources == null) { - throw new NullPointerException("sources == null"); - } - - if (exceptions == null) { - throw new NullPointerException("exceptions == null"); - } - - if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) { - throw new IllegalArgumentException("bogus branchingness"); - } - - if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) { - throw new IllegalArgumentException("exceptions / branchingness " + - "mismatch"); - } - - this.opcode = opcode; - this.result = result; - this.sources = sources; - this.exceptions = exceptions; - this.branchingness = branchingness; - this.isCallLike = isCallLike; - this.nickname = nickname; + /** minimum {@code BRANCH_*} value */ + public static final int BRANCH_MIN = 1; + + /** indicates a non-branching op */ + public static final int BRANCH_NONE = 1; + + /** indicates a function/method return */ + public static final int BRANCH_RETURN = 2; + + /** indicates an unconditional goto */ + public static final int BRANCH_GOTO = 3; + + /** indicates a two-way branch */ + public static final int BRANCH_IF = 4; + + /** indicates a switch-style branch */ + public static final int BRANCH_SWITCH = 5; + + /** indicates a throw-style branch (both always-throws and may-throw) */ + public static final int BRANCH_THROW = 6; + + /** maximum {@code BRANCH_*} value */ + public static final int BRANCH_MAX = 6; + + /** the opcode; one of the constants in {@link RegOps} */ + private final int opcode; + + /** + * {@code non-null;} result type of this operation; {@link Type#VOID} for + * no-result operations + */ + private final Type result; + + /** {@code non-null;} types of all the sources of this operation */ + private final TypeList sources; + + /** {@code non-null;} list of possible types thrown by this operation */ + private final TypeList exceptions; + + /** + * the branchingness of this op; one of the {@code BRANCH_*} + * constants in this class + */ + private final int branchingness; + + /** whether this is a function/method call op or similar */ + private final boolean isCallLike; + + /** {@code null-ok;} nickname, if specified (used for debugging) */ + private final String nickname; + + /** + * Constructs an instance. This method is private. Use one of the + * public constructors. + * + * @param opcode the opcode; one of the constants in {@link RegOps} + * @param result {@code non-null;} result type of this operation; {@link + * Type#VOID} for no-result operations + * @param sources {@code non-null;} types of all the sources of this operation + * @param exceptions {@code non-null;} list of possible types thrown by this + * operation + * @param branchingness the branchingness of this op; one of the + * {@code BRANCH_*} constants + * @param isCallLike whether the op is a function/method call or similar + * @param nickname {@code null-ok;} optional nickname (used for debugging) + */ + public Rop(int opcode, + Type result, + TypeList sources, + TypeList exceptions, + int branchingness, + boolean isCallLike, + String nickname) { + if (result == null) { + throw new NullPointerException("result == null"); } - /** - * Constructs an instance. The constructed instance is never a - * call-like op (see {@link #isCallLike}). - * - * @param opcode the opcode; one of the constants in {@link RegOps} - * @param result {@code non-null;} result type of this operation; {@link - * Type#VOID} for no-result operations - * @param sources {@code non-null;} types of all the sources of this operation - * @param exceptions {@code non-null;} list of possible types thrown by this - * operation - * @param branchingness the branchingness of this op; one of the - * {@code BRANCH_*} constants - * @param nickname {@code null-ok;} optional nickname (used for debugging) - */ - public Rop(int opcode, Type result, TypeList sources, - TypeList exceptions, int branchingness, String nickname) { - this(opcode, result, sources, exceptions, branchingness, false, - nickname); + if (sources == null) { + throw new NullPointerException("sources == null"); } - /** - * Constructs a no-exception instance. The constructed instance is never a - * call-like op (see {@link #isCallLike}). - * - * @param opcode the opcode; one of the constants in {@link RegOps} - * @param result {@code non-null;} result type of this operation; {@link - * Type#VOID} for no-result operations - * @param sources {@code non-null;} types of all the sources of this operation - * @param branchingness the branchingness of this op; one of the - * {@code BRANCH_*} constants - * @param nickname {@code null-ok;} optional nickname (used for debugging) - */ - public Rop(int opcode, Type result, TypeList sources, int branchingness, - String nickname) { - this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false, - nickname); + if (exceptions == null) { + throw new NullPointerException("exceptions == null"); } - /** - * Constructs a non-branching no-exception instance. The - * {@code branchingness} is always {@code BRANCH_NONE}, - * and it is never a call-like op (see {@link #isCallLike}). - * - * @param opcode the opcode; one of the constants in {@link RegOps} - * @param result {@code non-null;} result type of this operation; {@link - * Type#VOID} for no-result operations - * @param sources {@code non-null;} types of all the sources of this operation - * @param nickname {@code null-ok;} optional nickname (used for debugging) - */ - public Rop(int opcode, Type result, TypeList sources, String nickname) { - this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE, - false, nickname); + if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) { + throw new IllegalArgumentException("bogus branchingness"); } - /** - * Constructs a non-empty exceptions instance. Its - * {@code branchingness} is always {@code BRANCH_THROW}, - * but it is never a call-like op (see {@link #isCallLike}). - * - * @param opcode the opcode; one of the constants in {@link RegOps} - * @param result {@code non-null;} result type of this operation; {@link - * Type#VOID} for no-result operations - * @param sources {@code non-null;} types of all the sources of this operation - * @param exceptions {@code non-null;} list of possible types thrown by this - * operation - * @param nickname {@code null-ok;} optional nickname (used for debugging) - */ - public Rop(int opcode, Type result, TypeList sources, TypeList exceptions, - String nickname) { - this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false, - nickname); + if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) { + throw new IllegalArgumentException("exceptions / branchingness " + "mismatch"); } - /** - * Constructs a non-nicknamed instance with non-empty exceptions, which - * is always a call-like op (see {@link #isCallLike}). Its - * {@code branchingness} is always {@code BRANCH_THROW}. - * - * @param opcode the opcode; one of the constants in {@link RegOps} - * @param sources {@code non-null;} types of all the sources of this operation - * @param exceptions {@code non-null;} list of possible types thrown by this - * operation - */ - public Rop(int opcode, TypeList sources, TypeList exceptions) { - this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true, - null); + this.opcode = opcode; + this.result = result; + this.sources = sources; + this.exceptions = exceptions; + this.branchingness = branchingness; + this.isCallLike = isCallLike; + this.nickname = nickname; + } + + /** + * Constructs an instance. The constructed instance is never a + * call-like op (see {@link #isCallLike}). + * + * @param opcode the opcode; one of the constants in {@link RegOps} + * @param result {@code non-null;} result type of this operation; {@link + * Type#VOID} for no-result operations + * @param sources {@code non-null;} types of all the sources of this operation + * @param exceptions {@code non-null;} list of possible types thrown by this + * operation + * @param branchingness the branchingness of this op; one of the + * {@code BRANCH_*} constants + * @param nickname {@code null-ok;} optional nickname (used for debugging) + */ + public Rop(int opcode, + Type result, + TypeList sources, + TypeList exceptions, + int branchingness, + String nickname) { + this(opcode, result, sources, exceptions, branchingness, false, nickname); + } + + /** + * Constructs a no-exception instance. The constructed instance is never a + * call-like op (see {@link #isCallLike}). + * + * @param opcode the opcode; one of the constants in {@link RegOps} + * @param result {@code non-null;} result type of this operation; {@link + * Type#VOID} for no-result operations + * @param sources {@code non-null;} types of all the sources of this operation + * @param branchingness the branchingness of this op; one of the + * {@code BRANCH_*} constants + * @param nickname {@code null-ok;} optional nickname (used for debugging) + */ + public Rop(int opcode, Type result, TypeList sources, int branchingness, String nickname) { + this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false, nickname); + } + + /** + * Constructs a non-branching no-exception instance. The + * {@code branchingness} is always {@code BRANCH_NONE}, + * and it is never a call-like op (see {@link #isCallLike}). + * + * @param opcode the opcode; one of the constants in {@link RegOps} + * @param result {@code non-null;} result type of this operation; {@link + * Type#VOID} for no-result operations + * @param sources {@code non-null;} types of all the sources of this operation + * @param nickname {@code null-ok;} optional nickname (used for debugging) + */ + public Rop(int opcode, Type result, TypeList sources, String nickname) { + this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE, false, nickname); + } + + /** + * Constructs a non-empty exceptions instance. Its + * {@code branchingness} is always {@code BRANCH_THROW}, + * but it is never a call-like op (see {@link #isCallLike}). + * + * @param opcode the opcode; one of the constants in {@link RegOps} + * @param result {@code non-null;} result type of this operation; {@link + * Type#VOID} for no-result operations + * @param sources {@code non-null;} types of all the sources of this operation + * @param exceptions {@code non-null;} list of possible types thrown by this + * operation + * @param nickname {@code null-ok;} optional nickname (used for debugging) + */ + public Rop(int opcode, Type result, TypeList sources, TypeList exceptions, String nickname) { + this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false, nickname); + } + + /** + * Constructs a non-nicknamed instance with non-empty exceptions, which + * is always a call-like op (see {@link #isCallLike}). Its + * {@code branchingness} is always {@code BRANCH_THROW}. + * + * @param opcode the opcode; one of the constants in {@link RegOps} + * @param sources {@code non-null;} types of all the sources of this operation + * @param exceptions {@code non-null;} list of possible types thrown by this + * operation + */ + public Rop(int opcode, TypeList sources, TypeList exceptions) { + this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true, null); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (this == other) { + // Easy out. + return true; } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (this == other) { - // Easy out. - return true; - } - - if (!(other instanceof Rop)) { - return false; - } - - Rop rop = (Rop) other; - - return (opcode == rop.opcode) && - (branchingness == rop.branchingness) && - (result == rop.result) && - sources.equals(rop.sources) && - exceptions.equals(rop.exceptions); + if (!(other instanceof Rop)) { + return false; } - /** {@inheritDoc} */ - @Override - public int hashCode() { - int h = (opcode * 31) + branchingness; - h = (h * 31) + result.hashCode(); - h = (h * 31) + sources.hashCode(); - h = (h * 31) + exceptions.hashCode(); - - return h; - } - - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(40); - - sb.append("Rop{"); - - sb.append(RegOps.opName(opcode)); + Rop rop = (Rop) other; - if (result != Type.VOID) { - sb.append(" "); - sb.append(result); - } else { - sb.append(" ."); - } - - sb.append(" <-"); - - int sz = sources.size(); - if (sz == 0) { - sb.append(" ."); - } else { - for (int i = 0; i < sz; i++) { - sb.append(' '); - sb.append(sources.getType(i)); - } - } - - if (isCallLike) { - sb.append(" call"); - } + return (opcode == rop.opcode) && (branchingness == rop.branchingness) && (result == rop.result) + && sources.equals(rop.sources) && exceptions.equals(rop.exceptions); + } - sz = exceptions.size(); - if (sz != 0) { - sb.append(" throws"); - for (int i = 0; i < sz; i++) { - sb.append(' '); - Type one = exceptions.getType(i); - if (one == Type.THROWABLE) { - sb.append("<any>"); - } else { - sb.append(exceptions.getType(i)); - } - } - } else { - switch (branchingness) { - case BRANCH_NONE: sb.append(" flows"); break; - case BRANCH_RETURN: sb.append(" returns"); break; - case BRANCH_GOTO: sb.append(" gotos"); break; - case BRANCH_IF: sb.append(" ifs"); break; - case BRANCH_SWITCH: sb.append(" switches"); break; - default: sb.append(" " + Hex.u1(branchingness)); break; - } - } + /** {@inheritDoc} */ + @Override + public int hashCode() { + int h = (opcode * 31) + branchingness; + h = (h * 31) + result.hashCode(); + h = (h * 31) + sources.hashCode(); + h = (h * 31) + exceptions.hashCode(); - sb.append('}'); + return h; + } - return sb.toString(); - } + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(40); - /** - * Gets the opcode. - * - * @return the opcode - */ - public int getOpcode() { - return opcode; - } + sb.append("Rop{"); - /** - * Gets the result type. A return value of {@link Type#VOID} - * means this operation returns nothing. - * - * @return {@code null-ok;} the result spec - */ - public Type getResult() { - return result; - } + sb.append(RegOps.opName(opcode)); - /** - * Gets the source types. - * - * @return {@code non-null;} the source types - */ - public TypeList getSources() { - return sources; + if (result != Type.VOID) { + sb.append(" "); + sb.append(result); + } else { + sb.append(" ."); } - /** - * Gets the list of exception types that might be thrown. - * - * @return {@code non-null;} the list of exception types - */ - public TypeList getExceptions() { - return exceptions; - } + sb.append(" <-"); - /** - * Gets the branchingness of this instance. - * - * @return the branchingness - */ - public int getBranchingness() { - return branchingness; + int sz = sources.size(); + if (sz == 0) { + sb.append(" ."); + } else { + for (int i = 0; i < sz; i++) { + sb.append(' '); + sb.append(sources.getType(i)); + } } - /** - * Gets whether this opcode is a function/method call or similar. - * - * @return {@code true} iff this opcode is call-like - */ - public boolean isCallLike() { - return isCallLike; + if (isCallLike) { + sb.append(" call"); } - - /** - * Gets whether this opcode is commutative (the order of its sources are - * unimportant) or not. All commutative Rops have exactly two sources and - * have no branchiness. - * - * @return true if rop is commutative - */ - public boolean isCommutative() { - switch (opcode) { - case RegOps.AND: - case RegOps.OR: - case RegOps.XOR: - case RegOps.ADD: - case RegOps.MUL: - return true; - default: - return false; + sz = exceptions.size(); + if (sz != 0) { + sb.append(" throws"); + for (int i = 0; i < sz; i++) { + sb.append(' '); + Type one = exceptions.getType(i); + if (one == Type.THROWABLE) { + sb.append("<any>"); + } else { + sb.append(exceptions.getType(i)); } + } + } else { + switch (branchingness) { + case BRANCH_NONE: + sb.append(" flows"); + break; + case BRANCH_RETURN: + sb.append(" returns"); + break; + case BRANCH_GOTO: + sb.append(" gotos"); + break; + case BRANCH_IF: + sb.append(" ifs"); + break; + case BRANCH_SWITCH: + sb.append(" switches"); + break; + default: + sb.append(" " + Hex.u1(branchingness)); + break; + } } - /** - * Gets the nickname. If this instance has no nickname, this returns - * the result of calling {@link #toString}. - * - * @return {@code non-null;} the nickname - */ - public String getNickname() { - if (nickname != null) { - return nickname; - } - - return toString(); + sb.append('}'); + + return sb.toString(); + } + + /** + * Gets the opcode. + * + * @return the opcode + */ + public int getOpcode() { + return opcode; + } + + /** + * Gets the result type. A return value of {@link Type#VOID} + * means this operation returns nothing. + * + * @return {@code null-ok;} the result spec + */ + public Type getResult() { + return result; + } + + /** + * Gets the source types. + * + * @return {@code non-null;} the source types + */ + public TypeList getSources() { + return sources; + } + + /** + * Gets the list of exception types that might be thrown. + * + * @return {@code non-null;} the list of exception types + */ + public TypeList getExceptions() { + return exceptions; + } + + /** + * Gets the branchingness of this instance. + * + * @return the branchingness + */ + public int getBranchingness() { + return branchingness; + } + + /** + * Gets whether this opcode is a function/method call or similar. + * + * @return {@code true} iff this opcode is call-like + */ + public boolean isCallLike() { + return isCallLike; + } + + + /** + * Gets whether this opcode is commutative (the order of its sources are + * unimportant) or not. All commutative Rops have exactly two sources and + * have no branchiness. + * + * @return true if rop is commutative + */ + public boolean isCommutative() { + switch (opcode) { + case RegOps.AND: + case RegOps.OR: + case RegOps.XOR: + case RegOps.ADD: + case RegOps.MUL: + return true; + default: + return false; } - - /** - * Gets whether this operation can possibly throw an exception. This - * is just a convenient wrapper for - * {@code getExceptions().size() != 0}. - * - * @return {@code true} iff this operation can possibly throw - */ - public final boolean canThrow() { - return (exceptions.size() != 0); + } + + /** + * Gets the nickname. If this instance has no nickname, this returns + * the result of calling {@link #toString}. + * + * @return {@code non-null;} the nickname + */ + public String getNickname() { + if (nickname != null) { + return nickname; } + + return toString(); + } + + /** + * Gets whether this operation can possibly throw an exception. This + * is just a convenient wrapper for + * {@code getExceptions().size() != 0}. + * + * @return {@code true} iff this operation can possibly throw + */ + public final boolean canThrow() { + return (exceptions.size() != 0); + } } diff --git a/dx/src/com/android/jack/dx/rop/code/RopMethod.java b/dx/src/com/android/jack/dx/rop/code/RopMethod.java index 412dc0d..3133652 100644 --- a/dx/src/com/android/jack/dx/rop/code/RopMethod.java +++ b/dx/src/com/android/jack/dx/rop/code/RopMethod.java @@ -16,7 +16,6 @@ package com.android.jack.dx.rop.code; -import com.android.jack.dx.util.Bits; import com.android.jack.dx.util.Hex; import com.android.jack.dx.util.IntList; @@ -24,184 +23,183 @@ import com.android.jack.dx.util.IntList; * All of the parts that make up a method at the rop layer. */ public final class RopMethod { - /** {@code non-null;} basic block list of the method */ - private final BasicBlockList blocks; - - /** {@code >= 0;} label for the block which starts the method */ - private final int firstLabel; - - /** - * {@code null-ok;} array of predecessors for each block, indexed by block - * label - */ - private IntList[] predecessors; - - /** - * {@code null-ok;} the predecessors for the implicit "exit" block, that is - * the labels for the blocks that return, if calculated - */ - private IntList exitPredecessors; - - /** - * Constructs an instance. - * - * @param blocks {@code non-null;} basic block list of the method - * @param firstLabel {@code >= 0;} the label of the first block to execute - */ - public RopMethod(BasicBlockList blocks, int firstLabel) { - if (blocks == null) { - throw new NullPointerException("blocks == null"); - } - - if (firstLabel < 0) { - throw new IllegalArgumentException("firstLabel < 0"); - } - - this.blocks = blocks; - this.firstLabel = firstLabel; - - this.predecessors = null; - this.exitPredecessors = null; + /** {@code non-null;} basic block list of the method */ + private final BasicBlockList blocks; + + /** {@code >= 0;} label for the block which starts the method */ + private final int firstLabel; + + /** + * {@code null-ok;} array of predecessors for each block, indexed by block + * label + */ + private IntList[] predecessors; + + /** + * {@code null-ok;} the predecessors for the implicit "exit" block, that is + * the labels for the blocks that return, if calculated + */ + private IntList exitPredecessors; + + /** + * Constructs an instance. + * + * @param blocks {@code non-null;} basic block list of the method + * @param firstLabel {@code >= 0;} the label of the first block to execute + */ + public RopMethod(BasicBlockList blocks, int firstLabel) { + if (blocks == null) { + throw new NullPointerException("blocks == null"); } - /** - * Gets the basic block list for this method. - * - * @return {@code non-null;} the list - */ - public BasicBlockList getBlocks() { - return blocks; + if (firstLabel < 0) { + throw new IllegalArgumentException("firstLabel < 0"); } - /** - * Gets the label for the first block in the method that this list - * represents. - * - * @return {@code >= 0;} the first-block label - */ - public int getFirstLabel() { - return firstLabel; + this.blocks = blocks; + this.firstLabel = firstLabel; + + this.predecessors = null; + this.exitPredecessors = null; + } + + /** + * Gets the basic block list for this method. + * + * @return {@code non-null;} the list + */ + public BasicBlockList getBlocks() { + return blocks; + } + + /** + * Gets the label for the first block in the method that this list + * represents. + * + * @return {@code >= 0;} the first-block label + */ + public int getFirstLabel() { + return firstLabel; + } + + /** + * Gets the predecessors associated with the given block. This throws + * an exception if there is no block with the given label. + * + * @param label {@code >= 0;} the label of the block in question + * @return {@code non-null;} the predecessors of that block + */ + public IntList labelToPredecessors(int label) { + if (exitPredecessors == null) { + calcPredecessors(); } - /** - * Gets the predecessors associated with the given block. This throws - * an exception if there is no block with the given label. - * - * @param label {@code >= 0;} the label of the block in question - * @return {@code non-null;} the predecessors of that block - */ - public IntList labelToPredecessors(int label) { - if (exitPredecessors == null) { - calcPredecessors(); - } + IntList result = predecessors[label]; - IntList result = predecessors[label]; + if (result == null) { + throw new RuntimeException("no such block: " + Hex.u2(label)); + } - if (result == null) { - throw new RuntimeException("no such block: " + Hex.u2(label)); - } + return result; + } + + /** + * Gets the exit predecessors for this instance. + * + * @return {@code non-null;} the exit predecessors + */ + public IntList getExitPredecessors() { + if (exitPredecessors == null) { + calcPredecessors(); + } - return result; + return exitPredecessors; + } + + + /** + * Returns an instance that is identical to this one, except that + * the registers in each instruction are offset by the given + * amount. + * + * @param delta the amount to offset register numbers by + * @return {@code non-null;} an appropriately-constructed instance + */ + public RopMethod withRegisterOffset(int delta) { + RopMethod result = new RopMethod(blocks.withRegisterOffset(delta), firstLabel); + + if (exitPredecessors != null) { + /* + * The predecessors have been calculated. It's safe to + * inject these into the new instance, since the + * transformation being applied doesn't affect the + * predecessors. + */ + result.exitPredecessors = exitPredecessors; + result.predecessors = predecessors; } - /** - * Gets the exit predecessors for this instance. - * - * @return {@code non-null;} the exit predecessors + return result; + } + + /** + * Calculates the predecessor sets for each block as well as for the + * exit. + */ + private void calcPredecessors() { + int maxLabel = blocks.getMaxLabel(); + IntList[] predecessors = new IntList[maxLabel]; + IntList exitPredecessors = new IntList(10); + int sz = blocks.size(); + + /* + * For each block, find its successors, and add the block's label to + * the successor's predecessors. */ - public IntList getExitPredecessors() { - if (exitPredecessors == null) { - calcPredecessors(); + for (int i = 0; i < sz; i++) { + BasicBlock one = blocks.get(i); + int label = one.getLabel(); + IntList successors = one.getSuccessors(); + int ssz = successors.size(); + if (ssz == 0) { + // This block exits. + exitPredecessors.add(label); + } else { + for (int j = 0; j < ssz; j++) { + int succLabel = successors.get(j); + IntList succPreds = predecessors[succLabel]; + if (succPreds == null) { + succPreds = new IntList(10); + predecessors[succLabel] = succPreds; + } + succPreds.add(label); } + } + } - return exitPredecessors; + // Sort and immutablize all the predecessor lists. + for (int i = 0; i < maxLabel; i++) { + IntList preds = predecessors[i]; + if (preds != null) { + preds.sort(); + preds.setImmutable(); + } } + exitPredecessors.sort(); + exitPredecessors.setImmutable(); - /** - * Returns an instance that is identical to this one, except that - * the registers in each instruction are offset by the given - * amount. - * - * @param delta the amount to offset register numbers by - * @return {@code non-null;} an appropriately-constructed instance + /* + * The start label might not ever have had any predecessors + * added to it (probably doesn't, because of how Java gets + * translated into rop form). So, check for this and rectify + * the situation if required. */ - public RopMethod withRegisterOffset(int delta) { - RopMethod result = new RopMethod(blocks.withRegisterOffset(delta), - firstLabel); - - if (exitPredecessors != null) { - /* - * The predecessors have been calculated. It's safe to - * inject these into the new instance, since the - * transformation being applied doesn't affect the - * predecessors. - */ - result.exitPredecessors = exitPredecessors; - result.predecessors = predecessors; - } - - return result; + if (predecessors[firstLabel] == null) { + predecessors[firstLabel] = IntList.EMPTY; } - /** - * Calculates the predecessor sets for each block as well as for the - * exit. - */ - private void calcPredecessors() { - int maxLabel = blocks.getMaxLabel(); - IntList[] predecessors = new IntList[maxLabel]; - IntList exitPredecessors = new IntList(10); - int sz = blocks.size(); - - /* - * For each block, find its successors, and add the block's label to - * the successor's predecessors. - */ - for (int i = 0; i < sz; i++) { - BasicBlock one = blocks.get(i); - int label = one.getLabel(); - IntList successors = one.getSuccessors(); - int ssz = successors.size(); - if (ssz == 0) { - // This block exits. - exitPredecessors.add(label); - } else { - for (int j = 0; j < ssz; j++) { - int succLabel = successors.get(j); - IntList succPreds = predecessors[succLabel]; - if (succPreds == null) { - succPreds = new IntList(10); - predecessors[succLabel] = succPreds; - } - succPreds.add(label); - } - } - } - - // Sort and immutablize all the predecessor lists. - for (int i = 0; i < maxLabel; i++) { - IntList preds = predecessors[i]; - if (preds != null) { - preds.sort(); - preds.setImmutable(); - } - } - - exitPredecessors.sort(); - exitPredecessors.setImmutable(); - - /* - * The start label might not ever have had any predecessors - * added to it (probably doesn't, because of how Java gets - * translated into rop form). So, check for this and rectify - * the situation if required. - */ - if (predecessors[firstLabel] == null) { - predecessors[firstLabel] = IntList.EMPTY; - } - - this.predecessors = predecessors; - this.exitPredecessors = exitPredecessors; - } + this.predecessors = predecessors; + this.exitPredecessors = exitPredecessors; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/Rops.java b/dx/src/com/android/jack/dx/rop/code/Rops.java index 23deb8d..22ba56e 100644 --- a/dx/src/com/android/jack/dx/rop/code/Rops.java +++ b/dx/src/com/android/jack/dx/rop/code/Rops.java @@ -30,2061 +30,2110 @@ import com.android.jack.dx.rop.type.TypeList; * Standard instances of {@link Rop}. */ public final class Rops { - /** {@code nop()} */ - public static final Rop NOP = - new Rop(RegOps.NOP, Type.VOID, StdTypeList.EMPTY, "nop"); - - /** {@code r,x: int :: r = x;} */ - public static final Rop MOVE_INT = - new Rop(RegOps.MOVE, Type.INT, StdTypeList.INT, "move-int"); - - /** {@code r,x: long :: r = x;} */ - public static final Rop MOVE_LONG = - new Rop(RegOps.MOVE, Type.LONG, StdTypeList.LONG, "move-long"); - - /** {@code r,x: float :: r = x;} */ - public static final Rop MOVE_FLOAT = - new Rop(RegOps.MOVE, Type.FLOAT, StdTypeList.FLOAT, "move-float"); - - /** {@code r,x: double :: r = x;} */ - public static final Rop MOVE_DOUBLE = - new Rop(RegOps.MOVE, Type.DOUBLE, StdTypeList.DOUBLE, "move-double"); - - /** {@code r,x: Object :: r = x;} */ - public static final Rop MOVE_OBJECT = - new Rop(RegOps.MOVE, Type.OBJECT, StdTypeList.OBJECT, "move-object"); - - /** - * {@code r,x: ReturnAddress :: r = x;} - * - * Note that this rop-form instruction has no dex-form equivilent and - * must be removed before the dex conversion. - */ - public static final Rop MOVE_RETURN_ADDRESS = - new Rop(RegOps.MOVE, Type.RETURN_ADDRESS, - StdTypeList.RETURN_ADDRESS, "move-return-address"); - - /** {@code r,param(x): int :: r = param(x);} */ - public static final Rop MOVE_PARAM_INT = - new Rop(RegOps.MOVE_PARAM, Type.INT, StdTypeList.EMPTY, - "move-param-int"); - - /** {@code r,param(x): long :: r = param(x);} */ - public static final Rop MOVE_PARAM_LONG = - new Rop(RegOps.MOVE_PARAM, Type.LONG, StdTypeList.EMPTY, - "move-param-long"); - - /** {@code r,param(x): float :: r = param(x);} */ - public static final Rop MOVE_PARAM_FLOAT = - new Rop(RegOps.MOVE_PARAM, Type.FLOAT, StdTypeList.EMPTY, - "move-param-float"); - - /** {@code r,param(x): double :: r = param(x);} */ - public static final Rop MOVE_PARAM_DOUBLE = - new Rop(RegOps.MOVE_PARAM, Type.DOUBLE, StdTypeList.EMPTY, - "move-param-double"); - - /** {@code r,param(x): Object :: r = param(x);} */ - public static final Rop MOVE_PARAM_OBJECT = - new Rop(RegOps.MOVE_PARAM, Type.OBJECT, StdTypeList.EMPTY, - "move-param-object"); - - /** {@code r, literal: int :: r = literal;} */ - public static final Rop CONST_INT = - new Rop(RegOps.CONST, Type.INT, StdTypeList.EMPTY, "const-int"); - - /** {@code r, literal: long :: r = literal;} */ - public static final Rop CONST_LONG = - new Rop(RegOps.CONST, Type.LONG, StdTypeList.EMPTY, "const-long"); - - /** {@code r, literal: float :: r = literal;} */ - public static final Rop CONST_FLOAT = - new Rop(RegOps.CONST, Type.FLOAT, StdTypeList.EMPTY, "const-float"); - - /** {@code r, literal: double :: r = literal;} */ - public static final Rop CONST_DOUBLE = - new Rop(RegOps.CONST, Type.DOUBLE, StdTypeList.EMPTY, "const-double"); - - /** {@code r, literal: Object :: r = literal;} */ - public static final Rop CONST_OBJECT = - new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "const-object"); - - /** {@code r, literal: Object :: r = literal;} */ - public static final Rop CONST_OBJECT_NOTHROW = - new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY, - "const-object-nothrow"); - - /** {@code goto label} */ - public static final Rop GOTO = - new Rop(RegOps.GOTO, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_GOTO, - "goto"); - - /** {@code x: int :: if (x == 0) goto label} */ - public static final Rop IF_EQZ_INT = - new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, - "if-eqz-int"); - - /** {@code x: int :: if (x != 0) goto label} */ - public static final Rop IF_NEZ_INT = - new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, - "if-nez-int"); - - /** {@code x: int :: if (x < 0) goto label} */ - public static final Rop IF_LTZ_INT = - new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, - "if-ltz-int"); - - /** {@code x: int :: if (x >= 0) goto label} */ - public static final Rop IF_GEZ_INT = - new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, - "if-gez-int"); - - /** {@code x: int :: if (x <= 0) goto label} */ - public static final Rop IF_LEZ_INT = - new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, - "if-lez-int"); - - /** {@code x: int :: if (x > 0) goto label} */ - public static final Rop IF_GTZ_INT = - new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, - "if-gtz-int"); - - /** {@code x: Object :: if (x == null) goto label} */ - public static final Rop IF_EQZ_OBJECT = - new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF, - "if-eqz-object"); - - /** {@code x: Object :: if (x != null) goto label} */ - public static final Rop IF_NEZ_OBJECT = - new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF, - "if-nez-object"); - - /** {@code x,y: int :: if (x == y) goto label} */ - public static final Rop IF_EQ_INT = - new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, - "if-eq-int"); - - /** {@code x,y: int :: if (x != y) goto label} */ - public static final Rop IF_NE_INT = - new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, - "if-ne-int"); - - /** {@code x,y: int :: if (x < y) goto label} */ - public static final Rop IF_LT_INT = - new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, - "if-lt-int"); - - /** {@code x,y: int :: if (x >= y) goto label} */ - public static final Rop IF_GE_INT = - new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, - "if-ge-int"); - - /** {@code x,y: int :: if (x <= y) goto label} */ - public static final Rop IF_LE_INT = - new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, - "if-le-int"); - - /** {@code x,y: int :: if (x > y) goto label} */ - public static final Rop IF_GT_INT = - new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, - "if-gt-int"); - - /** {@code x,y: Object :: if (x == y) goto label} */ - public static final Rop IF_EQ_OBJECT = - new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT_OBJECT, - Rop.BRANCH_IF, "if-eq-object"); - - /** {@code x,y: Object :: if (x != y) goto label} */ - public static final Rop IF_NE_OBJECT = - new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT_OBJECT, - Rop.BRANCH_IF, "if-ne-object"); - - /** {@code x: int :: goto switchtable[x]} */ - public static final Rop SWITCH = - new Rop(RegOps.SWITCH, Type.VOID, StdTypeList.INT, Rop.BRANCH_SWITCH, - "switch"); - - /** {@code r,x,y: int :: r = x + y;} */ - public static final Rop ADD_INT = - new Rop(RegOps.ADD, Type.INT, StdTypeList.INT_INT, "add-int"); - - /** {@code r,x,y: long :: r = x + y;} */ - public static final Rop ADD_LONG = - new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG_LONG, "add-long"); - - /** {@code r,x,y: float :: r = x + y;} */ - public static final Rop ADD_FLOAT = - new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "add-float"); - - /** {@code r,x,y: double :: r = x + y;} */ - public static final Rop ADD_DOUBLE = - new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, - Rop.BRANCH_NONE, "add-double"); - - /** {@code r,x,y: int :: r = x - y;} */ - public static final Rop SUB_INT = - new Rop(RegOps.SUB, Type.INT, StdTypeList.INT_INT, "sub-int"); - - /** {@code r,x,y: long :: r = x - y;} */ - public static final Rop SUB_LONG = - new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG_LONG, "sub-long"); - - /** {@code r,x,y: float :: r = x - y;} */ - public static final Rop SUB_FLOAT = - new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "sub-float"); - - /** {@code r,x,y: double :: r = x - y;} */ - public static final Rop SUB_DOUBLE = - new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, - Rop.BRANCH_NONE, "sub-double"); - - /** {@code r,x,y: int :: r = x * y;} */ - public static final Rop MUL_INT = - new Rop(RegOps.MUL, Type.INT, StdTypeList.INT_INT, "mul-int"); - - /** {@code r,x,y: long :: r = x * y;} */ - public static final Rop MUL_LONG = - new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG_LONG, "mul-long"); - - /** {@code r,x,y: float :: r = x * y;} */ - public static final Rop MUL_FLOAT = - new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "mul-float"); - - /** {@code r,x,y: double :: r = x * y;} */ - public static final Rop MUL_DOUBLE = - new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, - Rop.BRANCH_NONE, "mul-double"); - - /** {@code r,x,y: int :: r = x / y;} */ - public static final Rop DIV_INT = - new Rop(RegOps.DIV, Type.INT, StdTypeList.INT_INT, - Exceptions.LIST_Error_ArithmeticException, "div-int"); - - /** {@code r,x,y: long :: r = x / y;} */ - public static final Rop DIV_LONG = - new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG_LONG, - Exceptions.LIST_Error_ArithmeticException, "div-long"); - - /** {@code r,x,y: float :: r = x / y;} */ - public static final Rop DIV_FLOAT = - new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "div-float"); - - /** {@code r,x,y: double :: r = x / y;} */ - public static final Rop DIV_DOUBLE = - new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, - "div-double"); - - /** {@code r,x,y: int :: r = x % y;} */ - public static final Rop REM_INT = - new Rop(RegOps.REM, Type.INT, StdTypeList.INT_INT, - Exceptions.LIST_Error_ArithmeticException, "rem-int"); - - /** {@code r,x,y: long :: r = x % y;} */ - public static final Rop REM_LONG = - new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG_LONG, - Exceptions.LIST_Error_ArithmeticException, "rem-long"); - - /** {@code r,x,y: float :: r = x % y;} */ - public static final Rop REM_FLOAT = - new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "rem-float"); - - /** {@code r,x,y: double :: r = x % y;} */ - public static final Rop REM_DOUBLE = - new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, - "rem-double"); - - /** {@code r,x: int :: r = -x;} */ - public static final Rop NEG_INT = - new Rop(RegOps.NEG, Type.INT, StdTypeList.INT, "neg-int"); - - /** {@code r,x: long :: r = -x;} */ - public static final Rop NEG_LONG = - new Rop(RegOps.NEG, Type.LONG, StdTypeList.LONG, "neg-long"); - - /** {@code r,x: float :: r = -x;} */ - public static final Rop NEG_FLOAT = - new Rop(RegOps.NEG, Type.FLOAT, StdTypeList.FLOAT, "neg-float"); - - /** {@code r,x: double :: r = -x;} */ - public static final Rop NEG_DOUBLE = - new Rop(RegOps.NEG, Type.DOUBLE, StdTypeList.DOUBLE, "neg-double"); - - /** {@code r,x,y: int :: r = x & y;} */ - public static final Rop AND_INT = - new Rop(RegOps.AND, Type.INT, StdTypeList.INT_INT, "and-int"); - - /** {@code r,x,y: long :: r = x & y;} */ - public static final Rop AND_LONG = - new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG_LONG, "and-long"); - - /** {@code r,x,y: int :: r = x | y;} */ - public static final Rop OR_INT = - new Rop(RegOps.OR, Type.INT, StdTypeList.INT_INT, "or-int"); - - /** {@code r,x,y: long :: r = x | y;} */ - public static final Rop OR_LONG = - new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG_LONG, "or-long"); - - /** {@code r,x,y: int :: r = x ^ y;} */ - public static final Rop XOR_INT = - new Rop(RegOps.XOR, Type.INT, StdTypeList.INT_INT, "xor-int"); - - /** {@code r,x,y: long :: r = x ^ y;} */ - public static final Rop XOR_LONG = - new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG_LONG, "xor-long"); - - /** {@code r,x,y: int :: r = x << y;} */ - public static final Rop SHL_INT = - new Rop(RegOps.SHL, Type.INT, StdTypeList.INT_INT, "shl-int"); - - /** {@code r,x: long; y: int :: r = x << y;} */ - public static final Rop SHL_LONG = - new Rop(RegOps.SHL, Type.LONG, StdTypeList.LONG_INT, "shl-long"); - - /** {@code r,x,y: int :: r = x >> y;} */ - public static final Rop SHR_INT = - new Rop(RegOps.SHR, Type.INT, StdTypeList.INT_INT, "shr-int"); - - /** {@code r,x: long; y: int :: r = x >> y;} */ - public static final Rop SHR_LONG = - new Rop(RegOps.SHR, Type.LONG, StdTypeList.LONG_INT, "shr-long"); - - /** {@code r,x,y: int :: r = x >>> y;} */ - public static final Rop USHR_INT = - new Rop(RegOps.USHR, Type.INT, StdTypeList.INT_INT, "ushr-int"); - - /** {@code r,x: long; y: int :: r = x >>> y;} */ - public static final Rop USHR_LONG = - new Rop(RegOps.USHR, Type.LONG, StdTypeList.LONG_INT, "ushr-long"); - - /** {@code r,x: int :: r = ~x;} */ - public static final Rop NOT_INT = - new Rop(RegOps.NOT, Type.INT, StdTypeList.INT, "not-int"); - - /** {@code r,x: long :: r = ~x;} */ - public static final Rop NOT_LONG = - new Rop(RegOps.NOT, Type.LONG, StdTypeList.LONG, "not-long"); - - /** {@code r,x,c: int :: r = x + c;} */ - public static final Rop ADD_CONST_INT = - new Rop(RegOps.ADD, Type.INT, StdTypeList.INT, "add-const-int"); - - /** {@code r,x,c: long :: r = x + c;} */ - public static final Rop ADD_CONST_LONG = - new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG, "add-const-long"); - - /** {@code r,x,c: float :: r = x + c;} */ - public static final Rop ADD_CONST_FLOAT = - new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT, "add-const-float"); - - /** {@code r,x,c: double :: r = x + c;} */ - public static final Rop ADD_CONST_DOUBLE = - new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE, - "add-const-double"); - - /** {@code r,x,c: int :: r = x - c;} */ - public static final Rop SUB_CONST_INT = - new Rop(RegOps.SUB, Type.INT, StdTypeList.INT, "sub-const-int"); - - /** {@code r,x,c: long :: r = x - c;} */ - public static final Rop SUB_CONST_LONG = - new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG, "sub-const-long"); - - /** {@code r,x,c: float :: r = x - c;} */ - public static final Rop SUB_CONST_FLOAT = - new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT, "sub-const-float"); - - /** {@code r,x,c: double :: r = x - c;} */ - public static final Rop SUB_CONST_DOUBLE = - new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE, - "sub-const-double"); - - /** {@code r,x,c: int :: r = x * c;} */ - public static final Rop MUL_CONST_INT = - new Rop(RegOps.MUL, Type.INT, StdTypeList.INT, "mul-const-int"); - - /** {@code r,x,c: long :: r = x * c;} */ - public static final Rop MUL_CONST_LONG = - new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG, "mul-const-long"); - - /** {@code r,x,c: float :: r = x * c;} */ - public static final Rop MUL_CONST_FLOAT = - new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT, "mul-const-float"); - - /** {@code r,x,c: double :: r = x * c;} */ - public static final Rop MUL_CONST_DOUBLE = - new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE, - "mul-const-double"); - - /** {@code r,x,c: int :: r = x / c;} */ - public static final Rop DIV_CONST_INT = - new Rop(RegOps.DIV, Type.INT, StdTypeList.INT, - Exceptions.LIST_Error_ArithmeticException, "div-const-int"); - - /** {@code r,x,c: long :: r = x / c;} */ - public static final Rop DIV_CONST_LONG = - new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG, - Exceptions.LIST_Error_ArithmeticException, "div-const-long"); - - /** {@code r,x,c: float :: r = x / c;} */ - public static final Rop DIV_CONST_FLOAT = - new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT, "div-const-float"); - - /** {@code r,x,c: double :: r = x / c;} */ - public static final Rop DIV_CONST_DOUBLE = - new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE, - "div-const-double"); - - /** {@code r,x,c: int :: r = x % c;} */ - public static final Rop REM_CONST_INT = - new Rop(RegOps.REM, Type.INT, StdTypeList.INT, - Exceptions.LIST_Error_ArithmeticException, "rem-const-int"); - - /** {@code r,x,c: long :: r = x % c;} */ - public static final Rop REM_CONST_LONG = - new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG, - Exceptions.LIST_Error_ArithmeticException, "rem-const-long"); - - /** {@code r,x,c: float :: r = x % c;} */ - public static final Rop REM_CONST_FLOAT = - new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT, "rem-const-float"); - - /** {@code r,x,c: double :: r = x % c;} */ - public static final Rop REM_CONST_DOUBLE = - new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE, - "rem-const-double"); - - /** {@code r,x,c: int :: r = x & c;} */ - public static final Rop AND_CONST_INT = - new Rop(RegOps.AND, Type.INT, StdTypeList.INT, "and-const-int"); - - /** {@code r,x,c: long :: r = x & c;} */ - public static final Rop AND_CONST_LONG = - new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG, "and-const-long"); - - /** {@code r,x,c: int :: r = x | c;} */ - public static final Rop OR_CONST_INT = - new Rop(RegOps.OR, Type.INT, StdTypeList.INT, "or-const-int"); - - /** {@code r,x,c: long :: r = x | c;} */ - public static final Rop OR_CONST_LONG = - new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG, "or-const-long"); - - /** {@code r,x,c: int :: r = x ^ c;} */ - public static final Rop XOR_CONST_INT = - new Rop(RegOps.XOR, Type.INT, StdTypeList.INT, "xor-const-int"); - - /** {@code r,x,c: long :: r = x ^ c;} */ - public static final Rop XOR_CONST_LONG = - new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG, "xor-const-long"); - - /** {@code r,x,c: int :: r = x << c;} */ - public static final Rop SHL_CONST_INT = - new Rop(RegOps.SHL, Type.INT, StdTypeList.INT, "shl-const-int"); - - /** {@code r,x: long; c: int :: r = x << c;} */ - public static final Rop SHL_CONST_LONG = - new Rop(RegOps.SHL, Type.LONG, StdTypeList.INT, "shl-const-long"); - - /** {@code r,x,c: int :: r = x >> c;} */ - public static final Rop SHR_CONST_INT = - new Rop(RegOps.SHR, Type.INT, StdTypeList.INT, "shr-const-int"); - - /** {@code r,x: long; c: int :: r = x >> c;} */ - public static final Rop SHR_CONST_LONG = - new Rop(RegOps.SHR, Type.LONG, StdTypeList.INT, "shr-const-long"); - - /** {@code r,x,c: int :: r = x >>> c;} */ - public static final Rop USHR_CONST_INT = - new Rop(RegOps.USHR, Type.INT, StdTypeList.INT, "ushr-const-int"); - - /** {@code r,x: long; c: int :: r = x >>> c;} */ - public static final Rop USHR_CONST_LONG = - new Rop(RegOps.USHR, Type.LONG, StdTypeList.INT, "ushr-const-long"); - - /** {@code r: int; x,y: long :: r = cmp(x, y);} */ - public static final Rop CMPL_LONG = - new Rop(RegOps.CMPL, Type.INT, StdTypeList.LONG_LONG, "cmpl-long"); - - /** {@code r: int; x,y: float :: r = cmpl(x, y);} */ - public static final Rop CMPL_FLOAT = - new Rop(RegOps.CMPL, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpl-float"); - - /** {@code r: int; x,y: double :: r = cmpl(x, y);} */ - public static final Rop CMPL_DOUBLE = - new Rop(RegOps.CMPL, Type.INT, StdTypeList.DOUBLE_DOUBLE, - "cmpl-double"); - - /** {@code r: int; x,y: float :: r = cmpg(x, y);} */ - public static final Rop CMPG_FLOAT = - new Rop(RegOps.CMPG, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpg-float"); - - /** {@code r: int; x,y: double :: r = cmpg(x, y);} */ - public static final Rop CMPG_DOUBLE = - new Rop(RegOps.CMPG, Type.INT, StdTypeList.DOUBLE_DOUBLE, - "cmpg-double"); - - /** {@code r: int; x: long :: r = (int) x} */ - public static final Rop CONV_L2I = - new Rop(RegOps.CONV, Type.INT, StdTypeList.LONG, "conv-l2i"); - - /** {@code r: int; x: float :: r = (int) x} */ - public static final Rop CONV_F2I = - new Rop(RegOps.CONV, Type.INT, StdTypeList.FLOAT, "conv-f2i"); - - /** {@code r: int; x: double :: r = (int) x} */ - public static final Rop CONV_D2I = - new Rop(RegOps.CONV, Type.INT, StdTypeList.DOUBLE, "conv-d2i"); - - /** {@code r: long; x: int :: r = (long) x} */ - public static final Rop CONV_I2L = - new Rop(RegOps.CONV, Type.LONG, StdTypeList.INT, "conv-i2l"); - - /** {@code r: long; x: float :: r = (long) x} */ - public static final Rop CONV_F2L = - new Rop(RegOps.CONV, Type.LONG, StdTypeList.FLOAT, "conv-f2l"); - - /** {@code r: long; x: double :: r = (long) x} */ - public static final Rop CONV_D2L = - new Rop(RegOps.CONV, Type.LONG, StdTypeList.DOUBLE, "conv-d2l"); - - /** {@code r: float; x: int :: r = (float) x} */ - public static final Rop CONV_I2F = - new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.INT, "conv-i2f"); - - /** {@code r: float; x: long :: r = (float) x} */ - public static final Rop CONV_L2F = - new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.LONG, "conv-l2f"); - - /** {@code r: float; x: double :: r = (float) x} */ - public static final Rop CONV_D2F = - new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.DOUBLE, "conv-d2f"); - - /** {@code r: double; x: int :: r = (double) x} */ - public static final Rop CONV_I2D = - new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.INT, "conv-i2d"); - - /** {@code r: double; x: long :: r = (double) x} */ - public static final Rop CONV_L2D = - new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.LONG, "conv-l2d"); - - /** {@code r: double; x: float :: r = (double) x} */ - public static final Rop CONV_F2D = - new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.FLOAT, "conv-f2d"); - - /** - * {@code r,x: int :: r = (x << 24) >> 24} (Java-style - * convert int to byte) - */ - public static final Rop TO_BYTE = - new Rop(RegOps.TO_BYTE, Type.INT, StdTypeList.INT, "to-byte"); - - /** - * {@code r,x: int :: r = x & 0xffff} (Java-style - * convert int to char) - */ - public static final Rop TO_CHAR = - new Rop(RegOps.TO_CHAR, Type.INT, StdTypeList.INT, "to-char"); - - /** - * {@code r,x: int :: r = (x << 16) >> 16} (Java-style - * convert int to short) - */ - public static final Rop TO_SHORT = - new Rop(RegOps.TO_SHORT, Type.INT, StdTypeList.INT, "to-short"); - - /** {@code return void} */ - public static final Rop RETURN_VOID = - new Rop(RegOps.RETURN, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_RETURN, - "return-void"); - - /** {@code x: int; return x} */ - public static final Rop RETURN_INT = - new Rop(RegOps.RETURN, Type.VOID, StdTypeList.INT, Rop.BRANCH_RETURN, - "return-int"); - - /** {@code x: long; return x} */ - public static final Rop RETURN_LONG = - new Rop(RegOps.RETURN, Type.VOID, StdTypeList.LONG, Rop.BRANCH_RETURN, - "return-long"); - - /** {@code x: float; return x} */ - public static final Rop RETURN_FLOAT = - new Rop(RegOps.RETURN, Type.VOID, StdTypeList.FLOAT, Rop.BRANCH_RETURN, - "return-float"); - - /** {@code x: double; return x} */ - public static final Rop RETURN_DOUBLE = - new Rop(RegOps.RETURN, Type.VOID, StdTypeList.DOUBLE, - Rop.BRANCH_RETURN, "return-double"); - - /** {@code x: Object; return x} */ - public static final Rop RETURN_OBJECT = - new Rop(RegOps.RETURN, Type.VOID, StdTypeList.OBJECT, - Rop.BRANCH_RETURN, "return-object"); - - /** {@code T: any type; r: int; x: T[]; :: r = x.length} */ - public static final Rop ARRAY_LENGTH = - new Rop(RegOps.ARRAY_LENGTH, Type.INT, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, "array-length"); - - /** {@code x: Throwable :: throw(x)} */ - public static final Rop THROW = - new Rop(RegOps.THROW, Type.VOID, StdTypeList.THROWABLE, - StdTypeList.THROWABLE, "throw"); - - /** {@code x: Object :: monitorenter(x)} */ - public static final Rop MONITOR_ENTER = - new Rop(RegOps.MONITOR_ENTER, Type.VOID, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, "monitor-enter"); - - /** {@code x: Object :: monitorexit(x)} */ - public static final Rop MONITOR_EXIT = - new Rop(RegOps.MONITOR_EXIT, Type.VOID, StdTypeList.OBJECT, - Exceptions.LIST_Error_Null_IllegalMonitorStateException, - "monitor-exit"); - - /** {@code r,y: int; x: int[] :: r = x[y]} */ - public static final Rop AGET_INT = - new Rop(RegOps.AGET, Type.INT, StdTypeList.INTARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aget-int"); - - /** {@code r: long; x: long[]; y: int :: r = x[y]} */ - public static final Rop AGET_LONG = - new Rop(RegOps.AGET, Type.LONG, StdTypeList.LONGARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aget-long"); - - /** {@code r: float; x: float[]; y: int :: r = x[y]} */ - public static final Rop AGET_FLOAT = - new Rop(RegOps.AGET, Type.FLOAT, StdTypeList.FLOATARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aget-float"); - - /** {@code r: double; x: double[]; y: int :: r = x[y]} */ - public static final Rop AGET_DOUBLE = - new Rop(RegOps.AGET, Type.DOUBLE, StdTypeList.DOUBLEARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aget-double"); - - /** {@code r: Object; x: Object[]; y: int :: r = x[y]} */ - public static final Rop AGET_OBJECT = - new Rop(RegOps.AGET, Type.OBJECT, StdTypeList.OBJECTARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aget-object"); - - /** {@code r: boolean; x: boolean[]; y: int :: r = x[y]} */ - public static final Rop AGET_BOOLEAN = - new Rop(RegOps.AGET, Type.INT, StdTypeList.BOOLEANARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aget-boolean"); - - /** {@code r: byte; x: byte[]; y: int :: r = x[y]} */ - public static final Rop AGET_BYTE = - new Rop(RegOps.AGET, Type.INT, StdTypeList.BYTEARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-byte"); - - /** {@code r: char; x: char[]; y: int :: r = x[y]} */ - public static final Rop AGET_CHAR = - new Rop(RegOps.AGET, Type.INT, StdTypeList.CHARARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-char"); - - /** {@code r: short; x: short[]; y: int :: r = x[y]} */ - public static final Rop AGET_SHORT = - new Rop(RegOps.AGET, Type.INT, StdTypeList.SHORTARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aget-short"); - - /** {@code x,z: int; y: int[] :: y[z] = x} */ - public static final Rop APUT_INT = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_INTARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-int"); - - /** {@code x: long; y: long[]; z: int :: y[z] = x} */ - public static final Rop APUT_LONG = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.LONG_LONGARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-long"); - - /** {@code x: float; y: float[]; z: int :: y[z] = x} */ - public static final Rop APUT_FLOAT = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.FLOAT_FLOATARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aput-float"); - - /** {@code x: double; y: double[]; z: int :: y[z] = x} */ - public static final Rop APUT_DOUBLE = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.DOUBLE_DOUBLEARR_INT, - Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, - "aput-double"); - - /** {@code x: Object; y: Object[]; z: int :: y[z] = x} */ - public static final Rop APUT_OBJECT = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.OBJECT_OBJECTARR_INT, - Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, - "aput-object"); - - /** {@code x: boolean; y: boolean[]; z: int :: y[z] = x} */ - public static final Rop APUT_BOOLEAN = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BOOLEANARR_INT, - Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, - "aput-boolean"); - - /** {@code x: byte; y: byte[]; z: int :: y[z] = x} */ - public static final Rop APUT_BYTE = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BYTEARR_INT, - Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-byte"); - - /** {@code x: char; y: char[]; z: int :: y[z] = x} */ - public static final Rop APUT_CHAR = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_CHARARR_INT, - Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-char"); - - /** {@code x: short; y: short[]; z: int :: y[z] = x} */ - public static final Rop APUT_SHORT = - new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_SHORTARR_INT, - Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, - "aput-short"); - - /** - * {@code T: any non-array object type :: r = - * alloc(T)} (allocate heap space for an object) - */ - public static final Rop NEW_INSTANCE = - new Rop(RegOps.NEW_INSTANCE, Type.OBJECT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "new-instance"); - - /** {@code r: int[]; x: int :: r = new int[x]} */ - public static final Rop NEW_ARRAY_INT = - new Rop(RegOps.NEW_ARRAY, Type.INT_ARRAY, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-int"); - - /** {@code r: long[]; x: int :: r = new long[x]} */ - public static final Rop NEW_ARRAY_LONG = - new Rop(RegOps.NEW_ARRAY, Type.LONG_ARRAY, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-long"); - - /** {@code r: float[]; x: int :: r = new float[x]} */ - public static final Rop NEW_ARRAY_FLOAT = - new Rop(RegOps.NEW_ARRAY, Type.FLOAT_ARRAY, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-float"); - - /** {@code r: double[]; x: int :: r = new double[x]} */ - public static final Rop NEW_ARRAY_DOUBLE = - new Rop(RegOps.NEW_ARRAY, Type.DOUBLE_ARRAY, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-double"); - - /** {@code r: boolean[]; x: int :: r = new boolean[x]} */ - public static final Rop NEW_ARRAY_BOOLEAN = - new Rop(RegOps.NEW_ARRAY, Type.BOOLEAN_ARRAY, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-boolean"); - - /** {@code r: byte[]; x: int :: r = new byte[x]} */ - public static final Rop NEW_ARRAY_BYTE = - new Rop(RegOps.NEW_ARRAY, Type.BYTE_ARRAY, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-byte"); - - /** {@code r: char[]; x: int :: r = new char[x]} */ - public static final Rop NEW_ARRAY_CHAR = - new Rop(RegOps.NEW_ARRAY, Type.CHAR_ARRAY, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-char"); - - /** {@code r: short[]; x: int :: r = new short[x]} */ - public static final Rop NEW_ARRAY_SHORT = - new Rop(RegOps.NEW_ARRAY, Type.SHORT_ARRAY, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-short"); - - /** - * {@code T: any non-array object type; x: Object :: (T) x} (can - * throw {@code ClassCastException}) - */ - public static final Rop CHECK_CAST = - new Rop(RegOps.CHECK_CAST, Type.VOID, StdTypeList.OBJECT, - Exceptions.LIST_Error_ClassCastException, "check-cast"); - - /** - * {@code T: any non-array object type; x: Object :: x instanceof - * T}. Note: This is listed as throwing {@code Error} - * explicitly because the op <i>can</i> throw, but there are no - * other predefined exceptions for it. - */ - public static final Rop INSTANCE_OF = - new Rop(RegOps.INSTANCE_OF, Type.INT, StdTypeList.OBJECT, - Exceptions.LIST_Error, "instance-of"); - - /** - * {@code r: int; x: Object; f: instance field spec of - * type int :: r = x.f} - */ - public static final Rop GET_FIELD_INT = - new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, "get-field-int"); - - /** - * {@code r: long; x: Object; f: instance field spec of - * type long :: r = x.f} - */ - public static final Rop GET_FIELD_LONG = - new Rop(RegOps.GET_FIELD, Type.LONG, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, "get-field-long"); - - /** - * {@code r: float; x: Object; f: instance field spec of - * type float :: r = x.f} - */ - public static final Rop GET_FIELD_FLOAT = - new Rop(RegOps.GET_FIELD, Type.FLOAT, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, - "get-field-float"); - - /** - * {@code r: double; x: Object; f: instance field spec of - * type double :: r = x.f} - */ - public static final Rop GET_FIELD_DOUBLE = - new Rop(RegOps.GET_FIELD, Type.DOUBLE, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, - "get-field-double"); - - /** - * {@code r: Object; x: Object; f: instance field spec of - * type Object :: r = x.f} - */ - public static final Rop GET_FIELD_OBJECT = - new Rop(RegOps.GET_FIELD, Type.OBJECT, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, - "get-field-object"); - - /** - * {@code r: boolean; x: Object; f: instance field spec of - * type boolean :: r = x.f} - */ - public static final Rop GET_FIELD_BOOLEAN = - new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, - "get-field-boolean"); - - /** - * {@code r: byte; x: Object; f: instance field spec of - * type byte :: r = x.f} - */ - public static final Rop GET_FIELD_BYTE = - new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, - "get-field-byte"); - - /** - * {@code r: char; x: Object; f: instance field spec of - * type char :: r = x.f} - */ - public static final Rop GET_FIELD_CHAR = - new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, - "get-field-char"); - - /** - * {@code r: short; x: Object; f: instance field spec of - * type short :: r = x.f} - */ - public static final Rop GET_FIELD_SHORT = - new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, - Exceptions.LIST_Error_NullPointerException, - "get-field-short"); - - /** {@code r: int; f: static field spec of type int :: r = f} */ - public static final Rop GET_STATIC_INT = - new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-static-int"); - - /** {@code r: long; f: static field spec of type long :: r = f} */ - public static final Rop GET_STATIC_LONG = - new Rop(RegOps.GET_STATIC, Type.LONG, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-static-long"); - - /** {@code r: float; f: static field spec of type float :: r = f} */ - public static final Rop GET_STATIC_FLOAT = - new Rop(RegOps.GET_STATIC, Type.FLOAT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-static-float"); - - /** {@code r: double; f: static field spec of type double :: r = f} */ - public static final Rop GET_STATIC_DOUBLE = - new Rop(RegOps.GET_STATIC, Type.DOUBLE, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-static-double"); - - /** {@code r: Object; f: static field spec of type Object :: r = f} */ - public static final Rop GET_STATIC_OBJECT = - new Rop(RegOps.GET_STATIC, Type.OBJECT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-static-object"); - - /** {@code r: boolean; f: static field spec of type boolean :: r = f} */ - public static final Rop GET_STATIC_BOOLEAN = - new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-field-boolean"); - - /** {@code r: byte; f: static field spec of type byte :: r = f} */ - public static final Rop GET_STATIC_BYTE = - new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-field-byte"); - - /** {@code r: char; f: static field spec of type char :: r = f} */ - public static final Rop GET_STATIC_CHAR = - new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-field-char"); - - /** {@code r: short; f: static field spec of type short :: r = f} */ - public static final Rop GET_STATIC_SHORT = - new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, - Exceptions.LIST_Error, "get-field-short"); - - /** - * {@code x: int; y: Object; f: instance field spec of type - * int :: y.f = x} - */ - public static final Rop PUT_FIELD_INT = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT, - Exceptions.LIST_Error_NullPointerException, "put-field-int"); - - /** - * {@code x: long; y: Object; f: instance field spec of type - * long :: y.f = x} - */ - public static final Rop PUT_FIELD_LONG = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.LONG_OBJECT, - Exceptions.LIST_Error_NullPointerException, "put-field-long"); - - /** - * {@code x: float; y: Object; f: instance field spec of type - * float :: y.f = x} - */ - public static final Rop PUT_FIELD_FLOAT = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.FLOAT_OBJECT, - Exceptions.LIST_Error_NullPointerException, - "put-field-float"); - - /** - * {@code x: double; y: Object; f: instance field spec of type - * double :: y.f = x} - */ - public static final Rop PUT_FIELD_DOUBLE = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.DOUBLE_OBJECT, - Exceptions.LIST_Error_NullPointerException, - "put-field-double"); - - /** - * {@code x: Object; y: Object; f: instance field spec of type - * Object :: y.f = x} - */ - public static final Rop PUT_FIELD_OBJECT = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.OBJECT_OBJECT, - Exceptions.LIST_Error_NullPointerException, - "put-field-object"); - - /** - * {@code x: int; y: Object; f: instance field spec of type - * boolean :: y.f = x} - */ - public static final Rop PUT_FIELD_BOOLEAN = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT, - Exceptions.LIST_Error_NullPointerException, - "put-field-boolean"); - - /** - * {@code x: int; y: Object; f: instance field spec of type - * byte :: y.f = x} - */ - public static final Rop PUT_FIELD_BYTE = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT, - Exceptions.LIST_Error_NullPointerException, - "put-field-byte"); - - /** - * {@code x: int; y: Object; f: instance field spec of type - * char :: y.f = x} - */ - public static final Rop PUT_FIELD_CHAR = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT, - Exceptions.LIST_Error_NullPointerException, - "put-field-char"); - - /** - * {@code x: int; y: Object; f: instance field spec of type - * short :: y.f = x} - */ - public static final Rop PUT_FIELD_SHORT = - new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT, - Exceptions.LIST_Error_NullPointerException, - "put-field-short"); - - /** {@code f: static field spec of type int; x: int :: f = x} */ - public static final Rop PUT_STATIC_INT = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, - Exceptions.LIST_Error, "put-static-int"); - - /** {@code f: static field spec of type long; x: long :: f = x} */ - public static final Rop PUT_STATIC_LONG = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.LONG, - Exceptions.LIST_Error, "put-static-long"); - - /** {@code f: static field spec of type float; x: float :: f = x} */ - public static final Rop PUT_STATIC_FLOAT = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.FLOAT, - Exceptions.LIST_Error, "put-static-float"); - - /** {@code f: static field spec of type double; x: double :: f = x} */ - public static final Rop PUT_STATIC_DOUBLE = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.DOUBLE, - Exceptions.LIST_Error, "put-static-double"); - - /** {@code f: static field spec of type Object; x: Object :: f = x} */ - public static final Rop PUT_STATIC_OBJECT = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.OBJECT, - Exceptions.LIST_Error, "put-static-object"); - - /** - * {@code f: static field spec of type boolean; x: boolean :: f = - * x} - */ - public static final Rop PUT_STATIC_BOOLEAN = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, - Exceptions.LIST_Error, "put-static-boolean"); - - /** {@code f: static field spec of type byte; x: byte :: f = x} */ - public static final Rop PUT_STATIC_BYTE = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, - Exceptions.LIST_Error, "put-static-byte"); - - /** {@code f: static field spec of type char; x: char :: f = x} */ - public static final Rop PUT_STATIC_CHAR = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, - Exceptions.LIST_Error, "put-static-char"); - - /** {@code f: static field spec of type short; x: short :: f = x} */ - public static final Rop PUT_STATIC_SHORT = - new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, - Exceptions.LIST_Error, "put-static-short"); - - /** {@code x: Int :: local variable begins in x} */ - public static final Rop MARK_LOCAL_INT = - new Rop (RegOps.MARK_LOCAL, Type.VOID, - StdTypeList.INT, "mark-local-int"); - - /** {@code x: Long :: local variable begins in x} */ - public static final Rop MARK_LOCAL_LONG = - new Rop (RegOps.MARK_LOCAL, Type.VOID, - StdTypeList.LONG, "mark-local-long"); - - /** {@code x: Float :: local variable begins in x} */ - public static final Rop MARK_LOCAL_FLOAT = - new Rop (RegOps.MARK_LOCAL, Type.VOID, - StdTypeList.FLOAT, "mark-local-float"); - - /** {@code x: Double :: local variable begins in x} */ - public static final Rop MARK_LOCAL_DOUBLE = - new Rop (RegOps.MARK_LOCAL, Type.VOID, - StdTypeList.DOUBLE, "mark-local-double"); - - /** {@code x: Object :: local variable begins in x} */ - public static final Rop MARK_LOCAL_OBJECT = - new Rop (RegOps.MARK_LOCAL, Type.VOID, - StdTypeList.OBJECT, "mark-local-object"); - - /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */ - public static final Rop FILL_ARRAY_DATA = - new Rop(RegOps.FILL_ARRAY_DATA, Type.VOID, StdTypeList.EMPTY, - "fill-array-data"); - - /** - * Returns the appropriate rop for the given opcode, destination, - * and sources. The result is typically, but not necessarily, a - * shared instance. - * - * <p><b>Note:</b> This method does not do complete error checking on - * its arguments, and so it may return an instance which seemed "right - * enough" even though in actuality the passed arguments don't quite - * match what is returned. TODO: Revisit this issue.</p> - * - * @param opcode the opcode - * @param dest {@code non-null;} destination (result) type, or - * {@link Type#VOID} if none - * @param sources {@code non-null;} list of source types - * @param cst {@code null-ok;} associated constant, if any - * @return {@code non-null;} an appropriate instance - */ - public static Rop ropFor(int opcode, TypeBearer dest, TypeList sources, - Constant cst) { - switch (opcode) { - case RegOps.NOP: return NOP; - case RegOps.MOVE: return opMove(dest); - case RegOps.MOVE_PARAM: return opMoveParam(dest); - case RegOps.MOVE_EXCEPTION: return opMoveException(dest); - case RegOps.CONST: return opConst(dest); - case RegOps.GOTO: return GOTO; - case RegOps.IF_EQ: return opIfEq(sources); - case RegOps.IF_NE: return opIfNe(sources); - case RegOps.IF_LT: return opIfLt(sources); - case RegOps.IF_GE: return opIfGe(sources); - case RegOps.IF_LE: return opIfLe(sources); - case RegOps.IF_GT: return opIfGt(sources); - case RegOps.SWITCH: return SWITCH; - case RegOps.ADD: return opAdd(sources); - case RegOps.SUB: return opSub(sources); - case RegOps.MUL: return opMul(sources); - case RegOps.DIV: return opDiv(sources); - case RegOps.REM: return opRem(sources); - case RegOps.NEG: return opNeg(dest); - case RegOps.AND: return opAnd(sources); - case RegOps.OR: return opOr(sources); - case RegOps.XOR: return opXor(sources); - case RegOps.SHL: return opShl(sources); - case RegOps.SHR: return opShr(sources); - case RegOps.USHR: return opUshr(sources); - case RegOps.NOT: return opNot(dest); - case RegOps.CMPL: return opCmpl(sources.getType(0)); - case RegOps.CMPG: return opCmpg(sources.getType(0)); - case RegOps.CONV: return opConv(dest, sources.getType(0)); - case RegOps.TO_BYTE: return TO_BYTE; - case RegOps.TO_CHAR: return TO_CHAR; - case RegOps.TO_SHORT: return TO_SHORT; - case RegOps.RETURN: { - if (sources.size() == 0) { - return RETURN_VOID; - } - return opReturn(sources.getType(0)); - } - case RegOps.ARRAY_LENGTH: return ARRAY_LENGTH; - case RegOps.THROW: return THROW; - case RegOps.MONITOR_ENTER: return MONITOR_ENTER; - case RegOps.MONITOR_EXIT: return MONITOR_EXIT; - case RegOps.AGET: { - Type source = sources.getType(0); - Type componentType; - if (source == Type.KNOWN_NULL) { - /* - * Treat a known-null as an array of the expected - * result type. - */ - componentType = dest.getType(); - } else { - componentType = source.getComponentType(); - } - return opAget(componentType); - } - case RegOps.APUT: { - Type source = sources.getType(1); - Type componentType; - if (source == Type.KNOWN_NULL) { - /* - * Treat a known-null as an array of the type being - * stored. - */ - componentType = sources.getType(0); - } else { - componentType = source.getComponentType(); - } - return opAput(componentType); - } - case RegOps.NEW_INSTANCE: return NEW_INSTANCE; - case RegOps.NEW_ARRAY: return opNewArray(dest.getType()); - case RegOps.CHECK_CAST: return CHECK_CAST; - case RegOps.INSTANCE_OF: return INSTANCE_OF; - case RegOps.GET_FIELD: return opGetField(dest); - case RegOps.GET_STATIC: return opGetStatic(dest); - case RegOps.PUT_FIELD: return opPutField(sources.getType(0)); - case RegOps.PUT_STATIC: return opPutStatic(sources.getType(0)); - case RegOps.INVOKE_STATIC: { - return opInvokeStatic(((CstMethodRef) cst).getPrototype()); - } - case RegOps.INVOKE_VIRTUAL: { - CstBaseMethodRef cstMeth = (CstMethodRef) cst; - Prototype meth = cstMeth.getPrototype(); - CstType definer = cstMeth.getDefiningClass(); - meth = meth.withFirstParameter(definer.getClassType()); - return opInvokeVirtual(meth); - } - case RegOps.INVOKE_SUPER: { - CstBaseMethodRef cstMeth = (CstMethodRef) cst; - Prototype meth = cstMeth.getPrototype(); - CstType definer = cstMeth.getDefiningClass(); - meth = meth.withFirstParameter(definer.getClassType()); - return opInvokeSuper(meth); - } - case RegOps.INVOKE_DIRECT: { - CstBaseMethodRef cstMeth = (CstMethodRef) cst; - Prototype meth = cstMeth.getPrototype(); - CstType definer = cstMeth.getDefiningClass(); - meth = meth.withFirstParameter(definer.getClassType()); - return opInvokeDirect(meth); - } - case RegOps.INVOKE_INTERFACE: { - CstBaseMethodRef cstMeth = (CstMethodRef) cst; - Prototype meth = cstMeth.getPrototype(); - CstType definer = cstMeth.getDefiningClass(); - meth = meth.withFirstParameter(definer.getClassType()); - return opInvokeInterface(meth); - } - } + /** {@code nop()} */ + public static final Rop NOP = new Rop(RegOps.NOP, Type.VOID, StdTypeList.EMPTY, "nop"); - throw new RuntimeException("unknown opcode " + RegOps.opName(opcode)); - } + /** {@code r,x: int :: r = x;} */ + public static final Rop MOVE_INT = new Rop(RegOps.MOVE, Type.INT, StdTypeList.INT, "move-int"); + + /** {@code r,x: long :: r = x;} */ + public static final Rop MOVE_LONG = + new Rop(RegOps.MOVE, Type.LONG, StdTypeList.LONG, "move-long"); + + /** {@code r,x: float :: r = x;} */ + public static final Rop MOVE_FLOAT = + new Rop(RegOps.MOVE, Type.FLOAT, StdTypeList.FLOAT, "move-float"); + + /** {@code r,x: double :: r = x;} */ + public static final Rop MOVE_DOUBLE = + new Rop(RegOps.MOVE, Type.DOUBLE, StdTypeList.DOUBLE, "move-double"); + + /** {@code r,x: Object :: r = x;} */ + public static final Rop MOVE_OBJECT = + new Rop(RegOps.MOVE, Type.OBJECT, StdTypeList.OBJECT, "move-object"); + + /** + * {@code r,x: ReturnAddress :: r = x;} + * + * Note that this rop-form instruction has no dex-form equivilent and + * must be removed before the dex conversion. + */ + public static final Rop MOVE_RETURN_ADDRESS = + new Rop(RegOps.MOVE, Type.RETURN_ADDRESS, StdTypeList.RETURN_ADDRESS, "move-return-address"); - /** - * Returns the appropriate {@code move} rop for the given type. The - * result is a shared instance. - * - * @param type {@code non-null;} type of value being moved - * @return {@code non-null;} an appropriate instance - */ - public static Rop opMove(TypeBearer type) { - switch (type.getBasicFrameType()) { - case Type.BT_INT: return MOVE_INT; - case Type.BT_LONG: return MOVE_LONG; - case Type.BT_FLOAT: return MOVE_FLOAT; - case Type.BT_DOUBLE: return MOVE_DOUBLE; - case Type.BT_OBJECT: return MOVE_OBJECT; - case Type.BT_ADDR: return MOVE_RETURN_ADDRESS; - } + /** {@code r,param(x): int :: r = param(x);} */ + public static final Rop MOVE_PARAM_INT = + new Rop(RegOps.MOVE_PARAM, Type.INT, StdTypeList.EMPTY, "move-param-int"); - return throwBadType(type); - } + /** {@code r,param(x): long :: r = param(x);} */ + public static final Rop MOVE_PARAM_LONG = + new Rop(RegOps.MOVE_PARAM, Type.LONG, StdTypeList.EMPTY, "move-param-long"); - /** - * Returns the appropriate {@code move-param} rop for the - * given type. The result is a shared instance. - * - * @param type {@code non-null;} type of value being moved - * @return {@code non-null;} an appropriate instance - */ - public static Rop opMoveParam(TypeBearer type) { - switch (type.getBasicFrameType()) { - case Type.BT_INT: return MOVE_PARAM_INT; - case Type.BT_LONG: return MOVE_PARAM_LONG; - case Type.BT_FLOAT: return MOVE_PARAM_FLOAT; - case Type.BT_DOUBLE: return MOVE_PARAM_DOUBLE; - case Type.BT_OBJECT: return MOVE_PARAM_OBJECT; - } + /** {@code r,param(x): float :: r = param(x);} */ + public static final Rop MOVE_PARAM_FLOAT = + new Rop(RegOps.MOVE_PARAM, Type.FLOAT, StdTypeList.EMPTY, "move-param-float"); - return throwBadType(type); - } + /** {@code r,param(x): double :: r = param(x);} */ + public static final Rop MOVE_PARAM_DOUBLE = + new Rop(RegOps.MOVE_PARAM, Type.DOUBLE, StdTypeList.EMPTY, "move-param-double"); - /** - * Returns the appropriate {@code move-exception} rop for the - * given type. The result may be a shared instance. - * - * @param type {@code non-null;} type of the exception - * @return {@code non-null;} an appropriate instance - */ - public static Rop opMoveException(TypeBearer type) { - return new Rop(RegOps.MOVE_EXCEPTION, type.getType(), - StdTypeList.EMPTY, (String) null); - } + /** {@code r,param(x): Object :: r = param(x);} */ + public static final Rop MOVE_PARAM_OBJECT = + new Rop(RegOps.MOVE_PARAM, Type.OBJECT, StdTypeList.EMPTY, "move-param-object"); - /** - * Returns the appropriate {@code move-result} rop for the - * given type. The result may be a shared instance. - * - * @param type {@code non-null;} type of the parameter - * @return {@code non-null;} an appropriate instance - */ - public static Rop opMoveResult(TypeBearer type) { - return new Rop(RegOps.MOVE_RESULT, type.getType(), - StdTypeList.EMPTY, (String) null); - } + /** {@code r, literal: int :: r = literal;} */ + public static final Rop CONST_INT = + new Rop(RegOps.CONST, Type.INT, StdTypeList.EMPTY, "const-int"); - /** - * Returns the appropriate {@code move-result-pseudo} rop for the - * given type. The result may be a shared instance. - * - * @param type {@code non-null;} type of the parameter - * @return {@code non-null;} an appropriate instance - */ - public static Rop opMoveResultPseudo(TypeBearer type) { - return new Rop(RegOps.MOVE_RESULT_PSEUDO, type.getType(), - StdTypeList.EMPTY, (String) null); - } + /** {@code r, literal: long :: r = literal;} */ + public static final Rop CONST_LONG = + new Rop(RegOps.CONST, Type.LONG, StdTypeList.EMPTY, "const-long"); - /** - * Returns the appropriate {@code const} rop for the given - * type. The result is a shared instance. - * - * @param type {@code non-null;} type of the constant - * @return {@code non-null;} an appropriate instance - */ - public static Rop opConst(TypeBearer type) { - if (type.getType() == Type.KNOWN_NULL) { - return CONST_OBJECT_NOTHROW; - } + /** {@code r, literal: float :: r = literal;} */ + public static final Rop CONST_FLOAT = + new Rop(RegOps.CONST, Type.FLOAT, StdTypeList.EMPTY, "const-float"); - switch (type.getBasicFrameType()) { - case Type.BT_INT: return CONST_INT; - case Type.BT_LONG: return CONST_LONG; - case Type.BT_FLOAT: return CONST_FLOAT; - case Type.BT_DOUBLE: return CONST_DOUBLE; - case Type.BT_OBJECT: return CONST_OBJECT; - } + /** {@code r, literal: double :: r = literal;} */ + public static final Rop CONST_DOUBLE = + new Rop(RegOps.CONST, Type.DOUBLE, StdTypeList.EMPTY, "const-double"); - return throwBadType(type); - } + /** {@code r, literal: Object :: r = literal;} */ + public static final Rop CONST_OBJECT = + new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY, Exceptions.LIST_Error, "const-object"); - /** - * Returns the appropriate {@code if-eq} rop for the given - * sources. The result is a shared instance. - * - * @param types {@code non-null;} source types - * @return {@code non-null;} an appropriate instance - */ - public static Rop opIfEq(TypeList types) { - return pickIf(types, IF_EQZ_INT, IF_EQZ_OBJECT, - IF_EQ_INT, IF_EQ_OBJECT); - } + /** {@code r, literal: Object :: r = literal;} */ + public static final Rop CONST_OBJECT_NOTHROW = + new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY, "const-object-nothrow"); - /** - * Returns the appropriate {@code if-ne} rop for the given - * sources. The result is a shared instance. - * - * @param types {@code non-null;} source types - * @return {@code non-null;} an appropriate instance - */ - public static Rop opIfNe(TypeList types) { - return pickIf(types, IF_NEZ_INT, IF_NEZ_OBJECT, - IF_NE_INT, IF_NE_OBJECT); - } + /** {@code goto label} */ + public static final Rop GOTO = + new Rop(RegOps.GOTO, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_GOTO, "goto"); - /** - * Returns the appropriate {@code if-lt} rop for the given - * sources. The result is a shared instance. - * - * @param types {@code non-null;} source types - * @return {@code non-null;} an appropriate instance - */ - public static Rop opIfLt(TypeList types) { - return pickIf(types, IF_LTZ_INT, null, IF_LT_INT, null); - } + /** {@code x: int :: if (x == 0) goto label} */ + public static final Rop IF_EQZ_INT = + new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, "if-eqz-int"); - /** - * Returns the appropriate {@code if-ge} rop for the given - * sources. The result is a shared instance. - * - * @param types {@code non-null;} source types - * @return {@code non-null;} an appropriate instance - */ - public static Rop opIfGe(TypeList types) { - return pickIf(types, IF_GEZ_INT, null, IF_GE_INT, null); - } + /** {@code x: int :: if (x != 0) goto label} */ + public static final Rop IF_NEZ_INT = + new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, "if-nez-int"); - /** - * Returns the appropriate {@code if-gt} rop for the given - * sources. The result is a shared instance. - * - * @param types {@code non-null;} source types - * @return {@code non-null;} an appropriate instance - */ - public static Rop opIfGt(TypeList types) { - return pickIf(types, IF_GTZ_INT, null, IF_GT_INT, null); - } + /** {@code x: int :: if (x < 0) goto label} */ + public static final Rop IF_LTZ_INT = + new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, "if-ltz-int"); - /** - * Returns the appropriate {@code if-le} rop for the given - * sources. The result is a shared instance. - * - * @param types {@code non-null;} source types - * @return {@code non-null;} an appropriate instance - */ - public static Rop opIfLe(TypeList types) { - return pickIf(types, IF_LEZ_INT, null, IF_LE_INT, null); - } + /** {@code x: int :: if (x >= 0) goto label} */ + public static final Rop IF_GEZ_INT = + new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, "if-gez-int"); - /** - * Helper for all the {@code if*}-related methods, which - * checks types and picks one of the four variants, throwing if - * there's a problem. - * - * @param types {@code non-null;} the types - * @param intZ {@code non-null;} the int-to-0 comparison - * @param objZ {@code null-ok;} the object-to-null comparison - * @param intInt {@code non-null;} the int-to-int comparison - * @param objObj {@code non-null;} the object-to-object comparison - * @return {@code non-null;} the appropriate instance - */ - private static Rop pickIf(TypeList types, Rop intZ, Rop objZ, Rop intInt, - Rop objObj) { - switch(types.size()) { - case 1: { - switch (types.getType(0).getBasicFrameType()) { - case Type.BT_INT: { - return intZ; - } - case Type.BT_OBJECT: { - if (objZ != null) { - return objZ; - } - } - } - break; - } - case 2: { - int bt = types.getType(0).getBasicFrameType(); - if (bt == types.getType(1).getBasicFrameType()) { - switch (bt) { - case Type.BT_INT: { - return intInt; - } - case Type.BT_OBJECT: { - if (objObj != null) { - return objObj; - } - } - } - } - break; - } - } + /** {@code x: int :: if (x <= 0) goto label} */ + public static final Rop IF_LEZ_INT = + new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, "if-lez-int"); - return throwBadTypes(types); - } + /** {@code x: int :: if (x > 0) goto label} */ + public static final Rop IF_GTZ_INT = + new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF, "if-gtz-int"); - /** - * Returns the appropriate {@code add} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opAdd(TypeList types) { - return pickBinaryOp(types, ADD_CONST_INT, ADD_CONST_LONG, - ADD_CONST_FLOAT, ADD_CONST_DOUBLE, ADD_INT, - ADD_LONG, ADD_FLOAT, ADD_DOUBLE); - } + /** {@code x: Object :: if (x == null) goto label} */ + public static final Rop IF_EQZ_OBJECT = + new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF, "if-eqz-object"); + + /** {@code x: Object :: if (x != null) goto label} */ + public static final Rop IF_NEZ_OBJECT = + new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF, "if-nez-object"); + + /** {@code x,y: int :: if (x == y) goto label} */ + public static final Rop IF_EQ_INT = + new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, "if-eq-int"); + + /** {@code x,y: int :: if (x != y) goto label} */ + public static final Rop IF_NE_INT = + new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, "if-ne-int"); + + /** {@code x,y: int :: if (x < y) goto label} */ + public static final Rop IF_LT_INT = + new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, "if-lt-int"); + + /** {@code x,y: int :: if (x >= y) goto label} */ + public static final Rop IF_GE_INT = + new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, "if-ge-int"); - /** - * Returns the appropriate {@code sub} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opSub(TypeList types) { - return pickBinaryOp(types, SUB_CONST_INT, SUB_CONST_LONG, - SUB_CONST_FLOAT, SUB_CONST_DOUBLE, SUB_INT, - SUB_LONG, SUB_FLOAT, SUB_DOUBLE); - } + /** {@code x,y: int :: if (x <= y) goto label} */ + public static final Rop IF_LE_INT = + new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, "if-le-int"); - /** - * Returns the appropriate {@code mul} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opMul(TypeList types) { - return pickBinaryOp(types, MUL_CONST_INT, MUL_CONST_LONG, - MUL_CONST_FLOAT, MUL_CONST_DOUBLE, MUL_INT, - MUL_LONG, MUL_FLOAT, MUL_DOUBLE); - } + /** {@code x,y: int :: if (x > y) goto label} */ + public static final Rop IF_GT_INT = + new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF, "if-gt-int"); - /** - * Returns the appropriate {@code div} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opDiv(TypeList types) { - return pickBinaryOp(types, DIV_CONST_INT, DIV_CONST_LONG, - DIV_CONST_FLOAT, DIV_CONST_DOUBLE, DIV_INT, - DIV_LONG, DIV_FLOAT, DIV_DOUBLE); - } + /** {@code x,y: Object :: if (x == y) goto label} */ + public static final Rop IF_EQ_OBJECT = + new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT_OBJECT, Rop.BRANCH_IF, "if-eq-object"); - /** - * Returns the appropriate {@code rem} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opRem(TypeList types) { - return pickBinaryOp(types, REM_CONST_INT, REM_CONST_LONG, - REM_CONST_FLOAT, REM_CONST_DOUBLE, REM_INT, - REM_LONG, REM_FLOAT, REM_DOUBLE); - } + /** {@code x,y: Object :: if (x != y) goto label} */ + public static final Rop IF_NE_OBJECT = + new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT_OBJECT, Rop.BRANCH_IF, "if-ne-object"); - /** - * Returns the appropriate {@code and} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opAnd(TypeList types) { - return pickBinaryOp(types, AND_CONST_INT, AND_CONST_LONG, null, null, - AND_INT, AND_LONG, null, null); - } + /** {@code x: int :: goto switchtable[x]} */ + public static final Rop SWITCH = + new Rop(RegOps.SWITCH, Type.VOID, StdTypeList.INT, Rop.BRANCH_SWITCH, "switch"); - /** - * Returns the appropriate {@code or} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opOr(TypeList types) { - return pickBinaryOp(types, OR_CONST_INT, OR_CONST_LONG, null, null, - OR_INT, OR_LONG, null, null); - } + /** {@code r,x,y: int :: r = x + y;} */ + public static final Rop ADD_INT = new Rop(RegOps.ADD, Type.INT, StdTypeList.INT_INT, "add-int"); - /** - * Returns the appropriate {@code xor} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opXor(TypeList types) { - return pickBinaryOp(types, XOR_CONST_INT, XOR_CONST_LONG, null, null, - XOR_INT, XOR_LONG, null, null); - } + /** {@code r,x,y: long :: r = x + y;} */ + public static final Rop ADD_LONG = + new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG_LONG, "add-long"); - /** - * Returns the appropriate {@code shl} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opShl(TypeList types) { - return pickBinaryOp(types, SHL_CONST_INT, SHL_CONST_LONG, null, null, - SHL_INT, SHL_LONG, null, null); - } + /** {@code r,x,y: float :: r = x + y;} */ + public static final Rop ADD_FLOAT = + new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "add-float"); - /** - * Returns the appropriate {@code shr} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opShr(TypeList types) { - return pickBinaryOp(types, SHR_CONST_INT, SHR_CONST_LONG, null, null, - SHR_INT, SHR_LONG, null, null); - } + /** {@code r,x,y: double :: r = x + y;} */ + public static final Rop ADD_DOUBLE = + new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, Rop.BRANCH_NONE, "add-double"); - /** - * Returns the appropriate {@code ushr} rop for the given - * types. The result is a shared instance. - * - * @param types {@code non-null;} types of the sources - * @return {@code non-null;} an appropriate instance - */ - public static Rop opUshr(TypeList types) { - return pickBinaryOp(types, USHR_CONST_INT, USHR_CONST_LONG, null, null, - USHR_INT, USHR_LONG, null, null); - } + /** {@code r,x,y: int :: r = x - y;} */ + public static final Rop SUB_INT = new Rop(RegOps.SUB, Type.INT, StdTypeList.INT_INT, "sub-int"); - /** - * Returns the appropriate binary arithmetic rop for the given type - * and arguments. The result is a shared instance. - * - * @param types {@code non-null;} sources of the operation - * @param int1 {@code non-null;} the int-to-constant rop - * @param long1 {@code non-null;} the long-to-constant rop - * @param float1 {@code null-ok;} the float-to-constant rop, if any - * @param double1 {@code null-ok;} the double-to-constant rop, if any - * @param int2 {@code non-null;} the int-to-int rop - * @param long2 {@code non-null;} the long-to-long or long-to-int rop - * @param float2 {@code null-ok;} the float-to-float rop, if any - * @param double2 {@code null-ok;} the double-to-double rop, if any - * @return {@code non-null;} an appropriate instance - */ - private static Rop pickBinaryOp(TypeList types, Rop int1, Rop long1, - Rop float1, Rop double1, Rop int2, - Rop long2, Rop float2, Rop double2) { - int bt1 = types.getType(0).getBasicFrameType(); - Rop result = null; - - switch (types.size()) { - case 1: { - switch(bt1) { - case Type.BT_INT: return int1; - case Type.BT_LONG: return long1; - case Type.BT_FLOAT: result = float1; break; - case Type.BT_DOUBLE: result = double1; break; - } - break; - } - case 2: { - switch(bt1) { - case Type.BT_INT: return int2; - case Type.BT_LONG: return long2; - case Type.BT_FLOAT: result = float2; break; - case Type.BT_DOUBLE: result = double2; break; - } - break; - } - } + /** {@code r,x,y: long :: r = x - y;} */ + public static final Rop SUB_LONG = + new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG_LONG, "sub-long"); - if (result == null) { - return throwBadTypes(types); - } + /** {@code r,x,y: float :: r = x - y;} */ + public static final Rop SUB_FLOAT = + new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "sub-float"); - return result; - } + /** {@code r,x,y: double :: r = x - y;} */ + public static final Rop SUB_DOUBLE = + new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, Rop.BRANCH_NONE, "sub-double"); - /** - * Returns the appropriate {@code neg} rop for the given type. The - * result is a shared instance. - * - * @param type {@code non-null;} type of value being operated on - * @return {@code non-null;} an appropriate instance - */ - public static Rop opNeg(TypeBearer type) { - switch (type.getBasicFrameType()) { - case Type.BT_INT: return NEG_INT; - case Type.BT_LONG: return NEG_LONG; - case Type.BT_FLOAT: return NEG_FLOAT; - case Type.BT_DOUBLE: return NEG_DOUBLE; - } + /** {@code r,x,y: int :: r = x * y;} */ + public static final Rop MUL_INT = new Rop(RegOps.MUL, Type.INT, StdTypeList.INT_INT, "mul-int"); - return throwBadType(type); - } + /** {@code r,x,y: long :: r = x * y;} */ + public static final Rop MUL_LONG = + new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG_LONG, "mul-long"); - /** - * Returns the appropriate {@code not} rop for the given type. The - * result is a shared instance. - * - * @param type {@code non-null;} type of value being operated on - * @return {@code non-null;} an appropriate instance - */ - public static Rop opNot(TypeBearer type) { - switch (type.getBasicFrameType()) { - case Type.BT_INT: return NOT_INT; - case Type.BT_LONG: return NOT_LONG; - } + /** {@code r,x,y: float :: r = x * y;} */ + public static final Rop MUL_FLOAT = + new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "mul-float"); - return throwBadType(type); - } + /** {@code r,x,y: double :: r = x * y;} */ + public static final Rop MUL_DOUBLE = + new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, Rop.BRANCH_NONE, "mul-double"); - /** - * Returns the appropriate {@code cmpl} rop for the given type. The - * result is a shared instance. - * - * @param type {@code non-null;} type of value being compared - * @return {@code non-null;} an appropriate instance - */ - public static Rop opCmpl(TypeBearer type) { - switch (type.getBasicType()) { - case Type.BT_LONG: return CMPL_LONG; - case Type.BT_FLOAT: return CMPL_FLOAT; - case Type.BT_DOUBLE: return CMPL_DOUBLE; - } + /** {@code r,x,y: int :: r = x / y;} */ + public static final Rop DIV_INT = new Rop(RegOps.DIV, Type.INT, StdTypeList.INT_INT, + Exceptions.LIST_Error_ArithmeticException, "div-int"); - return throwBadType(type); - } + /** {@code r,x,y: long :: r = x / y;} */ + public static final Rop DIV_LONG = new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG_LONG, + Exceptions.LIST_Error_ArithmeticException, "div-long"); - /** - * Returns the appropriate {@code cmpg} rop for the given type. The - * result is a shared instance. - * - * @param type {@code non-null;} type of value being compared - * @return {@code non-null;} an appropriate instance - */ - public static Rop opCmpg(TypeBearer type) { - switch (type.getBasicType()) { - case Type.BT_FLOAT: return CMPG_FLOAT; - case Type.BT_DOUBLE: return CMPG_DOUBLE; - } + /** {@code r,x,y: float :: r = x / y;} */ + public static final Rop DIV_FLOAT = + new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "div-float"); - return throwBadType(type); - } + /** {@code r,x,y: double :: r = x / y;} */ + public static final Rop DIV_DOUBLE = + new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, "div-double"); - /** - * Returns the appropriate {@code conv} rop for the given types. The - * result is a shared instance. - * - * @param dest {@code non-null;} target value type - * @param source {@code non-null;} source value type - * @return {@code non-null;} an appropriate instance - */ - public static Rop opConv(TypeBearer dest, TypeBearer source) { - int dbt = dest.getBasicFrameType(); - switch (source.getBasicFrameType()) { - case Type.BT_INT: { - switch (dbt) { - case Type.BT_LONG: return CONV_I2L; - case Type.BT_FLOAT: return CONV_I2F; - case Type.BT_DOUBLE: return CONV_I2D; - default: break; - } - } - case Type.BT_LONG: { - switch (dbt) { - case Type.BT_INT: return CONV_L2I; - case Type.BT_FLOAT: return CONV_L2F; - case Type.BT_DOUBLE: return CONV_L2D; - default: break; - } - } - case Type.BT_FLOAT: { - switch (dbt) { - case Type.BT_INT: return CONV_F2I; - case Type.BT_LONG: return CONV_F2L; - case Type.BT_DOUBLE: return CONV_F2D; - default: break; - } - } - case Type.BT_DOUBLE: { - switch (dbt) { - case Type.BT_INT: return CONV_D2I; - case Type.BT_LONG: return CONV_D2L; - case Type.BT_FLOAT: return CONV_D2F; - default: break; - } - } - } + /** {@code r,x,y: int :: r = x % y;} */ + public static final Rop REM_INT = new Rop(RegOps.REM, Type.INT, StdTypeList.INT_INT, + Exceptions.LIST_Error_ArithmeticException, "rem-int"); - return throwBadTypes(StdTypeList.make(dest.getType(), - source.getType())); - } + /** {@code r,x,y: long :: r = x % y;} */ + public static final Rop REM_LONG = new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG_LONG, + Exceptions.LIST_Error_ArithmeticException, "rem-long"); - /** - * Returns the appropriate {@code return} rop for the given type. The - * result is a shared instance. - * - * @param type {@code non-null;} type of value being returned - * @return {@code non-null;} an appropriate instance - */ - public static Rop opReturn(TypeBearer type) { - switch (type.getBasicFrameType()) { - case Type.BT_INT: return RETURN_INT; - case Type.BT_LONG: return RETURN_LONG; - case Type.BT_FLOAT: return RETURN_FLOAT; - case Type.BT_DOUBLE: return RETURN_DOUBLE; - case Type.BT_OBJECT: return RETURN_OBJECT; - case Type.BT_VOID: return RETURN_VOID; - } + /** {@code r,x,y: float :: r = x % y;} */ + public static final Rop REM_FLOAT = + new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "rem-float"); - return throwBadType(type); - } + /** {@code r,x,y: double :: r = x % y;} */ + public static final Rop REM_DOUBLE = + new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE, "rem-double"); + + /** {@code r,x: int :: r = -x;} */ + public static final Rop NEG_INT = new Rop(RegOps.NEG, Type.INT, StdTypeList.INT, "neg-int"); + + /** {@code r,x: long :: r = -x;} */ + public static final Rop NEG_LONG = new Rop(RegOps.NEG, Type.LONG, StdTypeList.LONG, "neg-long"); + + /** {@code r,x: float :: r = -x;} */ + public static final Rop NEG_FLOAT = + new Rop(RegOps.NEG, Type.FLOAT, StdTypeList.FLOAT, "neg-float"); + + /** {@code r,x: double :: r = -x;} */ + public static final Rop NEG_DOUBLE = + new Rop(RegOps.NEG, Type.DOUBLE, StdTypeList.DOUBLE, "neg-double"); + + /** {@code r,x,y: int :: r = x & y;} */ + public static final Rop AND_INT = new Rop(RegOps.AND, Type.INT, StdTypeList.INT_INT, "and-int"); + + /** {@code r,x,y: long :: r = x & y;} */ + public static final Rop AND_LONG = + new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG_LONG, "and-long"); + + /** {@code r,x,y: int :: r = x | y;} */ + public static final Rop OR_INT = new Rop(RegOps.OR, Type.INT, StdTypeList.INT_INT, "or-int"); + + /** {@code r,x,y: long :: r = x | y;} */ + public static final Rop OR_LONG = new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG_LONG, "or-long"); + + /** {@code r,x,y: int :: r = x ^ y;} */ + public static final Rop XOR_INT = new Rop(RegOps.XOR, Type.INT, StdTypeList.INT_INT, "xor-int"); + + /** {@code r,x,y: long :: r = x ^ y;} */ + public static final Rop XOR_LONG = + new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG_LONG, "xor-long"); + + /** {@code r,x,y: int :: r = x << y;} */ + public static final Rop SHL_INT = new Rop(RegOps.SHL, Type.INT, StdTypeList.INT_INT, "shl-int"); + + /** {@code r,x: long; y: int :: r = x << y;} */ + public static final Rop SHL_LONG = + new Rop(RegOps.SHL, Type.LONG, StdTypeList.LONG_INT, "shl-long"); + + /** {@code r,x,y: int :: r = x >> y;} */ + public static final Rop SHR_INT = new Rop(RegOps.SHR, Type.INT, StdTypeList.INT_INT, "shr-int"); + + /** {@code r,x: long; y: int :: r = x >> y;} */ + public static final Rop SHR_LONG = + new Rop(RegOps.SHR, Type.LONG, StdTypeList.LONG_INT, "shr-long"); + + /** {@code r,x,y: int :: r = x >>> y;} */ + public static final Rop USHR_INT = + new Rop(RegOps.USHR, Type.INT, StdTypeList.INT_INT, "ushr-int"); + + /** {@code r,x: long; y: int :: r = x >>> y;} */ + public static final Rop USHR_LONG = + new Rop(RegOps.USHR, Type.LONG, StdTypeList.LONG_INT, "ushr-long"); + + /** {@code r,x: int :: r = ~x;} */ + public static final Rop NOT_INT = new Rop(RegOps.NOT, Type.INT, StdTypeList.INT, "not-int"); + + /** {@code r,x: long :: r = ~x;} */ + public static final Rop NOT_LONG = new Rop(RegOps.NOT, Type.LONG, StdTypeList.LONG, "not-long"); + + /** {@code r,x,c: int :: r = x + c;} */ + public static final Rop ADD_CONST_INT = + new Rop(RegOps.ADD, Type.INT, StdTypeList.INT, "add-const-int"); + + /** {@code r,x,c: long :: r = x + c;} */ + public static final Rop ADD_CONST_LONG = + new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG, "add-const-long"); + + /** {@code r,x,c: float :: r = x + c;} */ + public static final Rop ADD_CONST_FLOAT = + new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT, "add-const-float"); - /** - * Returns the appropriate {@code aget} rop for the given type. The - * result is a shared instance. - * - * @param type {@code non-null;} element type of array being accessed - * @return {@code non-null;} an appropriate instance - */ - public static Rop opAget(TypeBearer type) { - switch (type.getBasicType()) { - case Type.BT_INT: return AGET_INT; - case Type.BT_LONG: return AGET_LONG; - case Type.BT_FLOAT: return AGET_FLOAT; - case Type.BT_DOUBLE: return AGET_DOUBLE; - case Type.BT_OBJECT: return AGET_OBJECT; - case Type.BT_BOOLEAN: return AGET_BOOLEAN; - case Type.BT_BYTE: return AGET_BYTE; - case Type.BT_CHAR: return AGET_CHAR; - case Type.BT_SHORT: return AGET_SHORT; + /** {@code r,x,c: double :: r = x + c;} */ + public static final Rop ADD_CONST_DOUBLE = + new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE, "add-const-double"); + + /** {@code r,x,c: int :: r = x - c;} */ + public static final Rop SUB_CONST_INT = + new Rop(RegOps.SUB, Type.INT, StdTypeList.INT, "sub-const-int"); + + /** {@code r,x,c: long :: r = x - c;} */ + public static final Rop SUB_CONST_LONG = + new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG, "sub-const-long"); + + /** {@code r,x,c: float :: r = x - c;} */ + public static final Rop SUB_CONST_FLOAT = + new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT, "sub-const-float"); + + /** {@code r,x,c: double :: r = x - c;} */ + public static final Rop SUB_CONST_DOUBLE = + new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE, "sub-const-double"); + + /** {@code r,x,c: int :: r = x * c;} */ + public static final Rop MUL_CONST_INT = + new Rop(RegOps.MUL, Type.INT, StdTypeList.INT, "mul-const-int"); + + /** {@code r,x,c: long :: r = x * c;} */ + public static final Rop MUL_CONST_LONG = + new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG, "mul-const-long"); + + /** {@code r,x,c: float :: r = x * c;} */ + public static final Rop MUL_CONST_FLOAT = + new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT, "mul-const-float"); + + /** {@code r,x,c: double :: r = x * c;} */ + public static final Rop MUL_CONST_DOUBLE = + new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE, "mul-const-double"); + + /** {@code r,x,c: int :: r = x / c;} */ + public static final Rop DIV_CONST_INT = new Rop(RegOps.DIV, Type.INT, StdTypeList.INT, + Exceptions.LIST_Error_ArithmeticException, "div-const-int"); + + /** {@code r,x,c: long :: r = x / c;} */ + public static final Rop DIV_CONST_LONG = new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG, + Exceptions.LIST_Error_ArithmeticException, "div-const-long"); + + /** {@code r,x,c: float :: r = x / c;} */ + public static final Rop DIV_CONST_FLOAT = + new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT, "div-const-float"); + + /** {@code r,x,c: double :: r = x / c;} */ + public static final Rop DIV_CONST_DOUBLE = + new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE, "div-const-double"); + + /** {@code r,x,c: int :: r = x % c;} */ + public static final Rop REM_CONST_INT = new Rop(RegOps.REM, Type.INT, StdTypeList.INT, + Exceptions.LIST_Error_ArithmeticException, "rem-const-int"); + + /** {@code r,x,c: long :: r = x % c;} */ + public static final Rop REM_CONST_LONG = new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG, + Exceptions.LIST_Error_ArithmeticException, "rem-const-long"); + + /** {@code r,x,c: float :: r = x % c;} */ + public static final Rop REM_CONST_FLOAT = + new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT, "rem-const-float"); + + /** {@code r,x,c: double :: r = x % c;} */ + public static final Rop REM_CONST_DOUBLE = + new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE, "rem-const-double"); + + /** {@code r,x,c: int :: r = x & c;} */ + public static final Rop AND_CONST_INT = + new Rop(RegOps.AND, Type.INT, StdTypeList.INT, "and-const-int"); + + /** {@code r,x,c: long :: r = x & c;} */ + public static final Rop AND_CONST_LONG = + new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG, "and-const-long"); + + /** {@code r,x,c: int :: r = x | c;} */ + public static final Rop OR_CONST_INT = + new Rop(RegOps.OR, Type.INT, StdTypeList.INT, "or-const-int"); + + /** {@code r,x,c: long :: r = x | c;} */ + public static final Rop OR_CONST_LONG = + new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG, "or-const-long"); + + /** {@code r,x,c: int :: r = x ^ c;} */ + public static final Rop XOR_CONST_INT = + new Rop(RegOps.XOR, Type.INT, StdTypeList.INT, "xor-const-int"); + + /** {@code r,x,c: long :: r = x ^ c;} */ + public static final Rop XOR_CONST_LONG = + new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG, "xor-const-long"); + + /** {@code r,x,c: int :: r = x << c;} */ + public static final Rop SHL_CONST_INT = + new Rop(RegOps.SHL, Type.INT, StdTypeList.INT, "shl-const-int"); + + /** {@code r,x: long; c: int :: r = x << c;} */ + public static final Rop SHL_CONST_LONG = + new Rop(RegOps.SHL, Type.LONG, StdTypeList.INT, "shl-const-long"); + + /** {@code r,x,c: int :: r = x >> c;} */ + public static final Rop SHR_CONST_INT = + new Rop(RegOps.SHR, Type.INT, StdTypeList.INT, "shr-const-int"); + + /** {@code r,x: long; c: int :: r = x >> c;} */ + public static final Rop SHR_CONST_LONG = + new Rop(RegOps.SHR, Type.LONG, StdTypeList.INT, "shr-const-long"); + + /** {@code r,x,c: int :: r = x >>> c;} */ + public static final Rop USHR_CONST_INT = + new Rop(RegOps.USHR, Type.INT, StdTypeList.INT, "ushr-const-int"); + + /** {@code r,x: long; c: int :: r = x >>> c;} */ + public static final Rop USHR_CONST_LONG = + new Rop(RegOps.USHR, Type.LONG, StdTypeList.INT, "ushr-const-long"); + + /** {@code r: int; x,y: long :: r = cmp(x, y);} */ + public static final Rop CMPL_LONG = + new Rop(RegOps.CMPL, Type.INT, StdTypeList.LONG_LONG, "cmpl-long"); + + /** {@code r: int; x,y: float :: r = cmpl(x, y);} */ + public static final Rop CMPL_FLOAT = + new Rop(RegOps.CMPL, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpl-float"); + + /** {@code r: int; x,y: double :: r = cmpl(x, y);} */ + public static final Rop CMPL_DOUBLE = + new Rop(RegOps.CMPL, Type.INT, StdTypeList.DOUBLE_DOUBLE, "cmpl-double"); + + /** {@code r: int; x,y: float :: r = cmpg(x, y);} */ + public static final Rop CMPG_FLOAT = + new Rop(RegOps.CMPG, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpg-float"); + + /** {@code r: int; x,y: double :: r = cmpg(x, y);} */ + public static final Rop CMPG_DOUBLE = + new Rop(RegOps.CMPG, Type.INT, StdTypeList.DOUBLE_DOUBLE, "cmpg-double"); + + /** {@code r: int; x: long :: r = (int) x} */ + public static final Rop CONV_L2I = new Rop(RegOps.CONV, Type.INT, StdTypeList.LONG, "conv-l2i"); + + /** {@code r: int; x: float :: r = (int) x} */ + public static final Rop CONV_F2I = new Rop(RegOps.CONV, Type.INT, StdTypeList.FLOAT, "conv-f2i"); + + /** {@code r: int; x: double :: r = (int) x} */ + public static final Rop CONV_D2I = new Rop(RegOps.CONV, Type.INT, StdTypeList.DOUBLE, "conv-d2i"); + + /** {@code r: long; x: int :: r = (long) x} */ + public static final Rop CONV_I2L = new Rop(RegOps.CONV, Type.LONG, StdTypeList.INT, "conv-i2l"); + + /** {@code r: long; x: float :: r = (long) x} */ + public static final Rop CONV_F2L = new Rop(RegOps.CONV, Type.LONG, StdTypeList.FLOAT, "conv-f2l"); + + /** {@code r: long; x: double :: r = (long) x} */ + public static final Rop CONV_D2L = + new Rop(RegOps.CONV, Type.LONG, StdTypeList.DOUBLE, "conv-d2l"); + + /** {@code r: float; x: int :: r = (float) x} */ + public static final Rop CONV_I2F = new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.INT, "conv-i2f"); + + /** {@code r: float; x: long :: r = (float) x} */ + public static final Rop CONV_L2F = new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.LONG, "conv-l2f"); + + /** {@code r: float; x: double :: r = (float) x} */ + public static final Rop CONV_D2F = + new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.DOUBLE, "conv-d2f"); + + /** {@code r: double; x: int :: r = (double) x} */ + public static final Rop CONV_I2D = new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.INT, "conv-i2d"); + + /** {@code r: double; x: long :: r = (double) x} */ + public static final Rop CONV_L2D = + new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.LONG, "conv-l2d"); + + /** {@code r: double; x: float :: r = (double) x} */ + public static final Rop CONV_F2D = + new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.FLOAT, "conv-f2d"); + + /** + * {@code r,x: int :: r = (x << 24) >> 24} (Java-style + * convert int to byte) + */ + public static final Rop TO_BYTE = new Rop(RegOps.TO_BYTE, Type.INT, StdTypeList.INT, "to-byte"); + + /** + * {@code r,x: int :: r = x & 0xffff} (Java-style + * convert int to char) + */ + public static final Rop TO_CHAR = new Rop(RegOps.TO_CHAR, Type.INT, StdTypeList.INT, "to-char"); + + /** + * {@code r,x: int :: r = (x << 16) >> 16} (Java-style + * convert int to short) + */ + public static final Rop TO_SHORT = + new Rop(RegOps.TO_SHORT, Type.INT, StdTypeList.INT, "to-short"); + + /** {@code return void} */ + public static final Rop RETURN_VOID = + new Rop(RegOps.RETURN, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_RETURN, "return-void"); + + /** {@code x: int; return x} */ + public static final Rop RETURN_INT = + new Rop(RegOps.RETURN, Type.VOID, StdTypeList.INT, Rop.BRANCH_RETURN, "return-int"); + + /** {@code x: long; return x} */ + public static final Rop RETURN_LONG = + new Rop(RegOps.RETURN, Type.VOID, StdTypeList.LONG, Rop.BRANCH_RETURN, "return-long"); + + /** {@code x: float; return x} */ + public static final Rop RETURN_FLOAT = + new Rop(RegOps.RETURN, Type.VOID, StdTypeList.FLOAT, Rop.BRANCH_RETURN, "return-float"); + + /** {@code x: double; return x} */ + public static final Rop RETURN_DOUBLE = + new Rop(RegOps.RETURN, Type.VOID, StdTypeList.DOUBLE, Rop.BRANCH_RETURN, "return-double"); + + /** {@code x: Object; return x} */ + public static final Rop RETURN_OBJECT = + new Rop(RegOps.RETURN, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_RETURN, "return-object"); + + /** {@code T: any type; r: int; x: T[]; :: r = x.length} */ + public static final Rop ARRAY_LENGTH = new Rop(RegOps.ARRAY_LENGTH, Type.INT, StdTypeList.OBJECT, + Exceptions.LIST_Error_NullPointerException, "array-length"); + + /** {@code x: Throwable :: throw(x)} */ + public static final Rop THROW = + new Rop(RegOps.THROW, Type.VOID, StdTypeList.THROWABLE, StdTypeList.THROWABLE, "throw"); + + /** {@code x: Object :: monitorenter(x)} */ + public static final Rop MONITOR_ENTER = new Rop(RegOps.MONITOR_ENTER, Type.VOID, + StdTypeList.OBJECT, Exceptions.LIST_Error_NullPointerException, "monitor-enter"); + + /** {@code x: Object :: monitorexit(x)} */ + public static final Rop MONITOR_EXIT = new Rop(RegOps.MONITOR_EXIT, Type.VOID, StdTypeList.OBJECT, + Exceptions.LIST_Error_Null_IllegalMonitorStateException, "monitor-exit"); + + /** {@code r,y: int; x: int[] :: r = x[y]} */ + public static final Rop AGET_INT = new Rop(RegOps.AGET, Type.INT, StdTypeList.INTARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-int"); + + /** {@code r: long; x: long[]; y: int :: r = x[y]} */ + public static final Rop AGET_LONG = new Rop(RegOps.AGET, Type.LONG, StdTypeList.LONGARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-long"); + + /** {@code r: float; x: float[]; y: int :: r = x[y]} */ + public static final Rop AGET_FLOAT = new Rop(RegOps.AGET, Type.FLOAT, StdTypeList.FLOATARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-float"); + + /** {@code r: double; x: double[]; y: int :: r = x[y]} */ + public static final Rop AGET_DOUBLE = new Rop(RegOps.AGET, Type.DOUBLE, StdTypeList.DOUBLEARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-double"); + + /** {@code r: Object; x: Object[]; y: int :: r = x[y]} */ + public static final Rop AGET_OBJECT = new Rop(RegOps.AGET, Type.OBJECT, StdTypeList.OBJECTARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-object"); + + /** {@code r: boolean; x: boolean[]; y: int :: r = x[y]} */ + public static final Rop AGET_BOOLEAN = new Rop(RegOps.AGET, Type.INT, StdTypeList.BOOLEANARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-boolean"); + + /** {@code r: byte; x: byte[]; y: int :: r = x[y]} */ + public static final Rop AGET_BYTE = new Rop(RegOps.AGET, Type.INT, StdTypeList.BYTEARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-byte"); + + /** {@code r: char; x: char[]; y: int :: r = x[y]} */ + public static final Rop AGET_CHAR = new Rop(RegOps.AGET, Type.INT, StdTypeList.CHARARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-char"); + + /** {@code r: short; x: short[]; y: int :: r = x[y]} */ + public static final Rop AGET_SHORT = new Rop(RegOps.AGET, Type.INT, StdTypeList.SHORTARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-short"); + + /** {@code x,z: int; y: int[] :: y[z] = x} */ + public static final Rop APUT_INT = new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_INTARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-int"); + + /** {@code x: long; y: long[]; z: int :: y[z] = x} */ + public static final Rop APUT_LONG = new Rop(RegOps.APUT, Type.VOID, StdTypeList.LONG_LONGARR_INT, + Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-long"); + + /** {@code x: float; y: float[]; z: int :: y[z] = x} */ + public static final Rop APUT_FLOAT = new Rop(RegOps.APUT, Type.VOID, + StdTypeList.FLOAT_FLOATARR_INT, Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, + "aput-float"); + + /** {@code x: double; y: double[]; z: int :: y[z] = x} */ + public static final Rop APUT_DOUBLE = new Rop(RegOps.APUT, Type.VOID, + StdTypeList.DOUBLE_DOUBLEARR_INT, Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, + "aput-double"); + + /** {@code x: Object; y: Object[]; z: int :: y[z] = x} */ + public static final Rop APUT_OBJECT = new Rop(RegOps.APUT, Type.VOID, + StdTypeList.OBJECT_OBJECTARR_INT, Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, + "aput-object"); + + /** {@code x: boolean; y: boolean[]; z: int :: y[z] = x} */ + public static final Rop APUT_BOOLEAN = new Rop(RegOps.APUT, Type.VOID, + StdTypeList.INT_BOOLEANARR_INT, Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, + "aput-boolean"); + + /** {@code x: byte; y: byte[]; z: int :: y[z] = x} */ + public static final Rop APUT_BYTE = new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BYTEARR_INT, + Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-byte"); + + /** {@code x: char; y: char[]; z: int :: y[z] = x} */ + public static final Rop APUT_CHAR = new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_CHARARR_INT, + Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-char"); + + /** {@code x: short; y: short[]; z: int :: y[z] = x} */ + public static final Rop APUT_SHORT = new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_SHORTARR_INT, + Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-short"); + + /** + * {@code T: any non-array object type :: r = + * alloc(T)} (allocate heap space for an object) + */ + public static final Rop NEW_INSTANCE = new Rop(RegOps.NEW_INSTANCE, Type.OBJECT, + StdTypeList.EMPTY, Exceptions.LIST_Error, "new-instance"); + + /** {@code r: int[]; x: int :: r = new int[x]} */ + public static final Rop NEW_ARRAY_INT = new Rop(RegOps.NEW_ARRAY, Type.INT_ARRAY, StdTypeList.INT, + Exceptions.LIST_Error_NegativeArraySizeException, "new-array-int"); + + /** {@code r: long[]; x: int :: r = new long[x]} */ + public static final Rop NEW_ARRAY_LONG = new Rop(RegOps.NEW_ARRAY, Type.LONG_ARRAY, + StdTypeList.INT, Exceptions.LIST_Error_NegativeArraySizeException, "new-array-long"); + + /** {@code r: float[]; x: int :: r = new float[x]} */ + public static final Rop NEW_ARRAY_FLOAT = new Rop(RegOps.NEW_ARRAY, Type.FLOAT_ARRAY, + StdTypeList.INT, Exceptions.LIST_Error_NegativeArraySizeException, "new-array-float"); + + /** {@code r: double[]; x: int :: r = new double[x]} */ + public static final Rop NEW_ARRAY_DOUBLE = new Rop(RegOps.NEW_ARRAY, Type.DOUBLE_ARRAY, + StdTypeList.INT, Exceptions.LIST_Error_NegativeArraySizeException, "new-array-double"); + + /** {@code r: boolean[]; x: int :: r = new boolean[x]} */ + public static final Rop NEW_ARRAY_BOOLEAN = new Rop(RegOps.NEW_ARRAY, Type.BOOLEAN_ARRAY, + StdTypeList.INT, Exceptions.LIST_Error_NegativeArraySizeException, "new-array-boolean"); + + /** {@code r: byte[]; x: int :: r = new byte[x]} */ + public static final Rop NEW_ARRAY_BYTE = new Rop(RegOps.NEW_ARRAY, Type.BYTE_ARRAY, + StdTypeList.INT, Exceptions.LIST_Error_NegativeArraySizeException, "new-array-byte"); + + /** {@code r: char[]; x: int :: r = new char[x]} */ + public static final Rop NEW_ARRAY_CHAR = new Rop(RegOps.NEW_ARRAY, Type.CHAR_ARRAY, + StdTypeList.INT, Exceptions.LIST_Error_NegativeArraySizeException, "new-array-char"); + + /** {@code r: short[]; x: int :: r = new short[x]} */ + public static final Rop NEW_ARRAY_SHORT = new Rop(RegOps.NEW_ARRAY, Type.SHORT_ARRAY, + StdTypeList.INT, Exceptions.LIST_Error_NegativeArraySizeException, "new-array-short"); + + /** + * {@code T: any non-array object type; x: Object :: (T) x} (can + * throw {@code ClassCastException}) + */ + public static final Rop CHECK_CAST = new Rop(RegOps.CHECK_CAST, Type.VOID, StdTypeList.OBJECT, + Exceptions.LIST_Error_ClassCastException, "check-cast"); + + /** + * {@code T: any non-array object type; x: Object :: x instanceof + * T}. Note: This is listed as throwing {@code Error} + * explicitly because the op <i>can</i> throw, but there are no + * other predefined exceptions for it. + */ + public static final Rop INSTANCE_OF = new Rop(RegOps.INSTANCE_OF, Type.INT, StdTypeList.OBJECT, + Exceptions.LIST_Error, "instance-of"); + + /** + * {@code r: int; x: Object; f: instance field spec of + * type int :: r = x.f} + */ + public static final Rop GET_FIELD_INT = new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, + Exceptions.LIST_Error_NullPointerException, "get-field-int"); + + /** + * {@code r: long; x: Object; f: instance field spec of + * type long :: r = x.f} + */ + public static final Rop GET_FIELD_LONG = new Rop(RegOps.GET_FIELD, Type.LONG, StdTypeList.OBJECT, + Exceptions.LIST_Error_NullPointerException, "get-field-long"); + + /** + * {@code r: float; x: Object; f: instance field spec of + * type float :: r = x.f} + */ + public static final Rop GET_FIELD_FLOAT = new Rop(RegOps.GET_FIELD, Type.FLOAT, + StdTypeList.OBJECT, Exceptions.LIST_Error_NullPointerException, "get-field-float"); + + /** + * {@code r: double; x: Object; f: instance field spec of + * type double :: r = x.f} + */ + public static final Rop GET_FIELD_DOUBLE = new Rop(RegOps.GET_FIELD, Type.DOUBLE, + StdTypeList.OBJECT, Exceptions.LIST_Error_NullPointerException, "get-field-double"); + + /** + * {@code r: Object; x: Object; f: instance field spec of + * type Object :: r = x.f} + */ + public static final Rop GET_FIELD_OBJECT = new Rop(RegOps.GET_FIELD, Type.OBJECT, + StdTypeList.OBJECT, Exceptions.LIST_Error_NullPointerException, "get-field-object"); + + /** + * {@code r: boolean; x: Object; f: instance field spec of + * type boolean :: r = x.f} + */ + public static final Rop GET_FIELD_BOOLEAN = new Rop(RegOps.GET_FIELD, Type.INT, + StdTypeList.OBJECT, Exceptions.LIST_Error_NullPointerException, "get-field-boolean"); + + /** + * {@code r: byte; x: Object; f: instance field spec of + * type byte :: r = x.f} + */ + public static final Rop GET_FIELD_BYTE = new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, + Exceptions.LIST_Error_NullPointerException, "get-field-byte"); + + /** + * {@code r: char; x: Object; f: instance field spec of + * type char :: r = x.f} + */ + public static final Rop GET_FIELD_CHAR = new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, + Exceptions.LIST_Error_NullPointerException, "get-field-char"); + + /** + * {@code r: short; x: Object; f: instance field spec of + * type short :: r = x.f} + */ + public static final Rop GET_FIELD_SHORT = new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT, + Exceptions.LIST_Error_NullPointerException, "get-field-short"); + + /** {@code r: int; f: static field spec of type int :: r = f} */ + public static final Rop GET_STATIC_INT = new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, + Exceptions.LIST_Error, "get-static-int"); + + /** {@code r: long; f: static field spec of type long :: r = f} */ + public static final Rop GET_STATIC_LONG = new Rop(RegOps.GET_STATIC, Type.LONG, StdTypeList.EMPTY, + Exceptions.LIST_Error, "get-static-long"); + + /** {@code r: float; f: static field spec of type float :: r = f} */ + public static final Rop GET_STATIC_FLOAT = new Rop(RegOps.GET_STATIC, Type.FLOAT, + StdTypeList.EMPTY, Exceptions.LIST_Error, "get-static-float"); + + /** {@code r: double; f: static field spec of type double :: r = f} */ + public static final Rop GET_STATIC_DOUBLE = new Rop(RegOps.GET_STATIC, Type.DOUBLE, + StdTypeList.EMPTY, Exceptions.LIST_Error, "get-static-double"); + + /** {@code r: Object; f: static field spec of type Object :: r = f} */ + public static final Rop GET_STATIC_OBJECT = new Rop(RegOps.GET_STATIC, Type.OBJECT, + StdTypeList.EMPTY, Exceptions.LIST_Error, "get-static-object"); + + /** {@code r: boolean; f: static field spec of type boolean :: r = f} */ + public static final Rop GET_STATIC_BOOLEAN = new Rop(RegOps.GET_STATIC, Type.INT, + StdTypeList.EMPTY, Exceptions.LIST_Error, "get-field-boolean"); + + /** {@code r: byte; f: static field spec of type byte :: r = f} */ + public static final Rop GET_STATIC_BYTE = new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, + Exceptions.LIST_Error, "get-field-byte"); + + /** {@code r: char; f: static field spec of type char :: r = f} */ + public static final Rop GET_STATIC_CHAR = new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, + Exceptions.LIST_Error, "get-field-char"); + + /** {@code r: short; f: static field spec of type short :: r = f} */ + public static final Rop GET_STATIC_SHORT = new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY, + Exceptions.LIST_Error, "get-field-short"); + + /** + * {@code x: int; y: Object; f: instance field spec of type + * int :: y.f = x} + */ + public static final Rop PUT_FIELD_INT = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.INT_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-int"); + + /** + * {@code x: long; y: Object; f: instance field spec of type + * long :: y.f = x} + */ + public static final Rop PUT_FIELD_LONG = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.LONG_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-long"); + + /** + * {@code x: float; y: Object; f: instance field spec of type + * float :: y.f = x} + */ + public static final Rop PUT_FIELD_FLOAT = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.FLOAT_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-float"); + + /** + * {@code x: double; y: Object; f: instance field spec of type + * double :: y.f = x} + */ + public static final Rop PUT_FIELD_DOUBLE = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.DOUBLE_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-double"); + + /** + * {@code x: Object; y: Object; f: instance field spec of type + * Object :: y.f = x} + */ + public static final Rop PUT_FIELD_OBJECT = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.OBJECT_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-object"); + + /** + * {@code x: int; y: Object; f: instance field spec of type + * boolean :: y.f = x} + */ + public static final Rop PUT_FIELD_BOOLEAN = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.INT_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-boolean"); + + /** + * {@code x: int; y: Object; f: instance field spec of type + * byte :: y.f = x} + */ + public static final Rop PUT_FIELD_BYTE = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.INT_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-byte"); + + /** + * {@code x: int; y: Object; f: instance field spec of type + * char :: y.f = x} + */ + public static final Rop PUT_FIELD_CHAR = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.INT_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-char"); + + /** + * {@code x: int; y: Object; f: instance field spec of type + * short :: y.f = x} + */ + public static final Rop PUT_FIELD_SHORT = new Rop(RegOps.PUT_FIELD, Type.VOID, + StdTypeList.INT_OBJECT, Exceptions.LIST_Error_NullPointerException, "put-field-short"); + + /** {@code f: static field spec of type int; x: int :: f = x} */ + public static final Rop PUT_STATIC_INT = new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, + Exceptions.LIST_Error, "put-static-int"); + + /** {@code f: static field spec of type long; x: long :: f = x} */ + public static final Rop PUT_STATIC_LONG = new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.LONG, + Exceptions.LIST_Error, "put-static-long"); + + /** {@code f: static field spec of type float; x: float :: f = x} */ + public static final Rop PUT_STATIC_FLOAT = new Rop(RegOps.PUT_STATIC, Type.VOID, + StdTypeList.FLOAT, Exceptions.LIST_Error, "put-static-float"); + + /** {@code f: static field spec of type double; x: double :: f = x} */ + public static final Rop PUT_STATIC_DOUBLE = new Rop(RegOps.PUT_STATIC, Type.VOID, + StdTypeList.DOUBLE, Exceptions.LIST_Error, "put-static-double"); + + /** {@code f: static field spec of type Object; x: Object :: f = x} */ + public static final Rop PUT_STATIC_OBJECT = new Rop(RegOps.PUT_STATIC, Type.VOID, + StdTypeList.OBJECT, Exceptions.LIST_Error, "put-static-object"); + + /** + * {@code f: static field spec of type boolean; x: boolean :: f = + * x} + */ + public static final Rop PUT_STATIC_BOOLEAN = new Rop(RegOps.PUT_STATIC, Type.VOID, + StdTypeList.INT, Exceptions.LIST_Error, "put-static-boolean"); + + /** {@code f: static field spec of type byte; x: byte :: f = x} */ + public static final Rop PUT_STATIC_BYTE = new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, + Exceptions.LIST_Error, "put-static-byte"); + + /** {@code f: static field spec of type char; x: char :: f = x} */ + public static final Rop PUT_STATIC_CHAR = new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, + Exceptions.LIST_Error, "put-static-char"); + + /** {@code f: static field spec of type short; x: short :: f = x} */ + public static final Rop PUT_STATIC_SHORT = new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT, + Exceptions.LIST_Error, "put-static-short"); + + /** {@code x: Int :: local variable begins in x} */ + public static final Rop MARK_LOCAL_INT = + new Rop(RegOps.MARK_LOCAL, Type.VOID, StdTypeList.INT, "mark-local-int"); + + /** {@code x: Long :: local variable begins in x} */ + public static final Rop MARK_LOCAL_LONG = + new Rop(RegOps.MARK_LOCAL, Type.VOID, StdTypeList.LONG, "mark-local-long"); + + /** {@code x: Float :: local variable begins in x} */ + public static final Rop MARK_LOCAL_FLOAT = + new Rop(RegOps.MARK_LOCAL, Type.VOID, StdTypeList.FLOAT, "mark-local-float"); + + /** {@code x: Double :: local variable begins in x} */ + public static final Rop MARK_LOCAL_DOUBLE = + new Rop(RegOps.MARK_LOCAL, Type.VOID, StdTypeList.DOUBLE, "mark-local-double"); + + /** {@code x: Object :: local variable begins in x} */ + public static final Rop MARK_LOCAL_OBJECT = + new Rop(RegOps.MARK_LOCAL, Type.VOID, StdTypeList.OBJECT, "mark-local-object"); + + /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */ + public static final Rop FILL_ARRAY_DATA = + new Rop(RegOps.FILL_ARRAY_DATA, Type.VOID, StdTypeList.EMPTY, "fill-array-data"); + + /** + * Returns the appropriate rop for the given opcode, destination, + * and sources. The result is typically, but not necessarily, a + * shared instance. + * + * <p><b>Note:</b> This method does not do complete error checking on + * its arguments, and so it may return an instance which seemed "right + * enough" even though in actuality the passed arguments don't quite + * match what is returned. TODO(dx team): Revisit this issue.</p> + * + * @param opcode the opcode + * @param dest {@code non-null;} destination (result) type, or + * {@link Type#VOID} if none + * @param sources {@code non-null;} list of source types + * @param cst {@code null-ok;} associated constant, if any + * @return {@code non-null;} an appropriate instance + */ + public static Rop ropFor(int opcode, TypeBearer dest, TypeList sources, Constant cst) { + switch (opcode) { + case RegOps.NOP: + return NOP; + case RegOps.MOVE: + return opMove(dest); + case RegOps.MOVE_PARAM: + return opMoveParam(dest); + case RegOps.MOVE_EXCEPTION: + return opMoveException(dest); + case RegOps.CONST: + return opConst(dest); + case RegOps.GOTO: + return GOTO; + case RegOps.IF_EQ: + return opIfEq(sources); + case RegOps.IF_NE: + return opIfNe(sources); + case RegOps.IF_LT: + return opIfLt(sources); + case RegOps.IF_GE: + return opIfGe(sources); + case RegOps.IF_LE: + return opIfLe(sources); + case RegOps.IF_GT: + return opIfGt(sources); + case RegOps.SWITCH: + return SWITCH; + case RegOps.ADD: + return opAdd(sources); + case RegOps.SUB: + return opSub(sources); + case RegOps.MUL: + return opMul(sources); + case RegOps.DIV: + return opDiv(sources); + case RegOps.REM: + return opRem(sources); + case RegOps.NEG: + return opNeg(dest); + case RegOps.AND: + return opAnd(sources); + case RegOps.OR: + return opOr(sources); + case RegOps.XOR: + return opXor(sources); + case RegOps.SHL: + return opShl(sources); + case RegOps.SHR: + return opShr(sources); + case RegOps.USHR: + return opUshr(sources); + case RegOps.NOT: + return opNot(dest); + case RegOps.CMPL: + return opCmpl(sources.getType(0)); + case RegOps.CMPG: + return opCmpg(sources.getType(0)); + case RegOps.CONV: + return opConv(dest, sources.getType(0)); + case RegOps.TO_BYTE: + return TO_BYTE; + case RegOps.TO_CHAR: + return TO_CHAR; + case RegOps.TO_SHORT: + return TO_SHORT; + case RegOps.RETURN: { + if (sources.size() == 0) { + return RETURN_VOID; } + return opReturn(sources.getType(0)); + } + case RegOps.ARRAY_LENGTH: + return ARRAY_LENGTH; + case RegOps.THROW: + return THROW; + case RegOps.MONITOR_ENTER: + return MONITOR_ENTER; + case RegOps.MONITOR_EXIT: + return MONITOR_EXIT; + case RegOps.AGET: { + Type source = sources.getType(0); + Type componentType; + if (source == Type.KNOWN_NULL) { + /* + * Treat a known-null as an array of the expected + * result type. + */ + componentType = dest.getType(); + } else { + componentType = source.getComponentType(); + } + return opAget(componentType); + } + case RegOps.APUT: { + Type source = sources.getType(1); + Type componentType; + if (source == Type.KNOWN_NULL) { + /* + * Treat a known-null as an array of the type being + * stored. + */ + componentType = sources.getType(0); + } else { + componentType = source.getComponentType(); + } + return opAput(componentType); + } + case RegOps.NEW_INSTANCE: + return NEW_INSTANCE; + case RegOps.NEW_ARRAY: + return opNewArray(dest.getType()); + case RegOps.CHECK_CAST: + return CHECK_CAST; + case RegOps.INSTANCE_OF: + return INSTANCE_OF; + case RegOps.GET_FIELD: + return opGetField(dest); + case RegOps.GET_STATIC: + return opGetStatic(dest); + case RegOps.PUT_FIELD: + return opPutField(sources.getType(0)); + case RegOps.PUT_STATIC: + return opPutStatic(sources.getType(0)); + case RegOps.INVOKE_STATIC: { + return opInvokeStatic(((CstMethodRef) cst).getPrototype()); + } + case RegOps.INVOKE_VIRTUAL: { + CstBaseMethodRef cstMeth = (CstMethodRef) cst; + Prototype meth = cstMeth.getPrototype(); + CstType definer = cstMeth.getDefiningClass(); + meth = meth.withFirstParameter(definer.getClassType()); + return opInvokeVirtual(meth); + } + case RegOps.INVOKE_SUPER: { + CstBaseMethodRef cstMeth = (CstMethodRef) cst; + Prototype meth = cstMeth.getPrototype(); + CstType definer = cstMeth.getDefiningClass(); + meth = meth.withFirstParameter(definer.getClassType()); + return opInvokeSuper(meth); + } + case RegOps.INVOKE_DIRECT: { + CstBaseMethodRef cstMeth = (CstMethodRef) cst; + Prototype meth = cstMeth.getPrototype(); + CstType definer = cstMeth.getDefiningClass(); + meth = meth.withFirstParameter(definer.getClassType()); + return opInvokeDirect(meth); + } + case RegOps.INVOKE_INTERFACE: { + CstBaseMethodRef cstMeth = (CstMethodRef) cst; + Prototype meth = cstMeth.getPrototype(); + CstType definer = cstMeth.getDefiningClass(); + meth = meth.withFirstParameter(definer.getClassType()); + return opInvokeInterface(meth); + } + } - return throwBadType(type); + throw new RuntimeException("unknown opcode " + RegOps.opName(opcode)); + } + + /** + * Returns the appropriate {@code move} rop for the given type. The + * result is a shared instance. + * + * @param type {@code non-null;} type of value being moved + * @return {@code non-null;} an appropriate instance + */ + public static Rop opMove(TypeBearer type) { + switch (type.getBasicFrameType()) { + case Type.BT_INT: + return MOVE_INT; + case Type.BT_LONG: + return MOVE_LONG; + case Type.BT_FLOAT: + return MOVE_FLOAT; + case Type.BT_DOUBLE: + return MOVE_DOUBLE; + case Type.BT_OBJECT: + return MOVE_OBJECT; + case Type.BT_ADDR: + return MOVE_RETURN_ADDRESS; } - /** - * Returns the appropriate {@code aput} rop for the given type. The - * result is a shared instance. - * - * @param type {@code non-null;} element type of array being accessed - * @return {@code non-null;} an appropriate instance - */ - public static Rop opAput(TypeBearer type) { - switch (type.getBasicType()) { - case Type.BT_INT: return APUT_INT; - case Type.BT_LONG: return APUT_LONG; - case Type.BT_FLOAT: return APUT_FLOAT; - case Type.BT_DOUBLE: return APUT_DOUBLE; - case Type.BT_OBJECT: return APUT_OBJECT; - case Type.BT_BOOLEAN: return APUT_BOOLEAN; - case Type.BT_BYTE: return APUT_BYTE; - case Type.BT_CHAR: return APUT_CHAR; - case Type.BT_SHORT: return APUT_SHORT; - } + return throwBadType(type); + } + + /** + * Returns the appropriate {@code move-param} rop for the + * given type. The result is a shared instance. + * + * @param type {@code non-null;} type of value being moved + * @return {@code non-null;} an appropriate instance + */ + public static Rop opMoveParam(TypeBearer type) { + switch (type.getBasicFrameType()) { + case Type.BT_INT: + return MOVE_PARAM_INT; + case Type.BT_LONG: + return MOVE_PARAM_LONG; + case Type.BT_FLOAT: + return MOVE_PARAM_FLOAT; + case Type.BT_DOUBLE: + return MOVE_PARAM_DOUBLE; + case Type.BT_OBJECT: + return MOVE_PARAM_OBJECT; + } - return throwBadType(type); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code move-exception} rop for the + * given type. The result may be a shared instance. + * + * @param type {@code non-null;} type of the exception + * @return {@code non-null;} an appropriate instance + */ + public static Rop opMoveException(TypeBearer type) { + return new Rop(RegOps.MOVE_EXCEPTION, type.getType(), StdTypeList.EMPTY, (String) null); + } + + /** + * Returns the appropriate {@code move-result} rop for the + * given type. The result may be a shared instance. + * + * @param type {@code non-null;} type of the parameter + * @return {@code non-null;} an appropriate instance + */ + public static Rop opMoveResult(TypeBearer type) { + return new Rop(RegOps.MOVE_RESULT, type.getType(), StdTypeList.EMPTY, (String) null); + } + + /** + * Returns the appropriate {@code move-result-pseudo} rop for the + * given type. The result may be a shared instance. + * + * @param type {@code non-null;} type of the parameter + * @return {@code non-null;} an appropriate instance + */ + public static Rop opMoveResultPseudo(TypeBearer type) { + return new Rop(RegOps.MOVE_RESULT_PSEUDO, type.getType(), StdTypeList.EMPTY, (String) null); + } + + /** + * Returns the appropriate {@code const} rop for the given + * type. The result is a shared instance. + * + * @param type {@code non-null;} type of the constant + * @return {@code non-null;} an appropriate instance + */ + public static Rop opConst(TypeBearer type) { + if (type.getType() == Type.KNOWN_NULL) { + return CONST_OBJECT_NOTHROW; } - /** - * Returns the appropriate {@code new-array} rop for the given - * type. The result is a shared instance. - * - * @param arrayType {@code non-null;} array type of array being created - * @return {@code non-null;} an appropriate instance - */ - public static Rop opNewArray(TypeBearer arrayType) { - Type type = arrayType.getType(); - Type elementType = type.getComponentType(); - - switch (elementType.getBasicType()) { - case Type.BT_INT: return NEW_ARRAY_INT; - case Type.BT_LONG: return NEW_ARRAY_LONG; - case Type.BT_FLOAT: return NEW_ARRAY_FLOAT; - case Type.BT_DOUBLE: return NEW_ARRAY_DOUBLE; - case Type.BT_BOOLEAN: return NEW_ARRAY_BOOLEAN; - case Type.BT_BYTE: return NEW_ARRAY_BYTE; - case Type.BT_CHAR: return NEW_ARRAY_CHAR; - case Type.BT_SHORT: return NEW_ARRAY_SHORT; + switch (type.getBasicFrameType()) { + case Type.BT_INT: + return CONST_INT; + case Type.BT_LONG: + return CONST_LONG; + case Type.BT_FLOAT: + return CONST_FLOAT; + case Type.BT_DOUBLE: + return CONST_DOUBLE; + case Type.BT_OBJECT: + return CONST_OBJECT; + } + + return throwBadType(type); + } + + /** + * Returns the appropriate {@code if-eq} rop for the given + * sources. The result is a shared instance. + * + * @param types {@code non-null;} source types + * @return {@code non-null;} an appropriate instance + */ + public static Rop opIfEq(TypeList types) { + return pickIf(types, IF_EQZ_INT, IF_EQZ_OBJECT, IF_EQ_INT, IF_EQ_OBJECT); + } + + /** + * Returns the appropriate {@code if-ne} rop for the given + * sources. The result is a shared instance. + * + * @param types {@code non-null;} source types + * @return {@code non-null;} an appropriate instance + */ + public static Rop opIfNe(TypeList types) { + return pickIf(types, IF_NEZ_INT, IF_NEZ_OBJECT, IF_NE_INT, IF_NE_OBJECT); + } + + /** + * Returns the appropriate {@code if-lt} rop for the given + * sources. The result is a shared instance. + * + * @param types {@code non-null;} source types + * @return {@code non-null;} an appropriate instance + */ + public static Rop opIfLt(TypeList types) { + return pickIf(types, IF_LTZ_INT, null, IF_LT_INT, null); + } + + /** + * Returns the appropriate {@code if-ge} rop for the given + * sources. The result is a shared instance. + * + * @param types {@code non-null;} source types + * @return {@code non-null;} an appropriate instance + */ + public static Rop opIfGe(TypeList types) { + return pickIf(types, IF_GEZ_INT, null, IF_GE_INT, null); + } + + /** + * Returns the appropriate {@code if-gt} rop for the given + * sources. The result is a shared instance. + * + * @param types {@code non-null;} source types + * @return {@code non-null;} an appropriate instance + */ + public static Rop opIfGt(TypeList types) { + return pickIf(types, IF_GTZ_INT, null, IF_GT_INT, null); + } + + /** + * Returns the appropriate {@code if-le} rop for the given + * sources. The result is a shared instance. + * + * @param types {@code non-null;} source types + * @return {@code non-null;} an appropriate instance + */ + public static Rop opIfLe(TypeList types) { + return pickIf(types, IF_LEZ_INT, null, IF_LE_INT, null); + } + + /** + * Helper for all the {@code if*}-related methods, which + * checks types and picks one of the four variants, throwing if + * there's a problem. + * + * @param types {@code non-null;} the types + * @param intZ {@code non-null;} the int-to-0 comparison + * @param objZ {@code null-ok;} the object-to-null comparison + * @param intInt {@code non-null;} the int-to-int comparison + * @param objObj {@code non-null;} the object-to-object comparison + * @return {@code non-null;} the appropriate instance + */ + private static Rop pickIf(TypeList types, Rop intZ, Rop objZ, Rop intInt, Rop objObj) { + switch (types.size()) { + case 1: { + switch (types.getType(0).getBasicFrameType()) { + case Type.BT_INT: { + return intZ; + } + case Type.BT_OBJECT: { + if (objZ != null) { + return objZ; + } + } + } + break; + } + case 2: { + int bt = types.getType(0).getBasicFrameType(); + if (bt == types.getType(1).getBasicFrameType()) { + switch (bt) { + case Type.BT_INT: { + return intInt; + } case Type.BT_OBJECT: { - return new Rop(RegOps.NEW_ARRAY, type, StdTypeList.INT, - Exceptions.LIST_Error_NegativeArraySizeException, - "new-array-object"); + if (objObj != null) { + return objObj; + } } + } } - - return throwBadType(type); + break; + } } - /** - * Returns the appropriate {@code filled-new-array} rop for the given - * type. The result may be a shared instance. - * - * @param arrayType {@code non-null;} type of array being created - * @param count {@code >= 0;} number of elements that the array should have - * @return {@code non-null;} an appropriate instance - */ - public static Rop opFilledNewArray(TypeBearer arrayType, int count) { - Type type = arrayType.getType(); - Type elementType = type.getComponentType(); - - if (elementType.isCategory2()) { - return throwBadType(arrayType); - } - - if (count < 0) { - throw new IllegalArgumentException("count < 0"); + return throwBadTypes(types); + } + + /** + * Returns the appropriate {@code add} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opAdd(TypeList types) { + return pickBinaryOp(types, + ADD_CONST_INT, + ADD_CONST_LONG, + ADD_CONST_FLOAT, + ADD_CONST_DOUBLE, + ADD_INT, + ADD_LONG, + ADD_FLOAT, + ADD_DOUBLE); + } + + /** + * Returns the appropriate {@code sub} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opSub(TypeList types) { + return pickBinaryOp(types, + SUB_CONST_INT, + SUB_CONST_LONG, + SUB_CONST_FLOAT, + SUB_CONST_DOUBLE, + SUB_INT, + SUB_LONG, + SUB_FLOAT, + SUB_DOUBLE); + } + + /** + * Returns the appropriate {@code mul} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opMul(TypeList types) { + return pickBinaryOp(types, + MUL_CONST_INT, + MUL_CONST_LONG, + MUL_CONST_FLOAT, + MUL_CONST_DOUBLE, + MUL_INT, + MUL_LONG, + MUL_FLOAT, + MUL_DOUBLE); + } + + /** + * Returns the appropriate {@code div} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opDiv(TypeList types) { + return pickBinaryOp(types, + DIV_CONST_INT, + DIV_CONST_LONG, + DIV_CONST_FLOAT, + DIV_CONST_DOUBLE, + DIV_INT, + DIV_LONG, + DIV_FLOAT, + DIV_DOUBLE); + } + + /** + * Returns the appropriate {@code rem} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opRem(TypeList types) { + return pickBinaryOp(types, + REM_CONST_INT, + REM_CONST_LONG, + REM_CONST_FLOAT, + REM_CONST_DOUBLE, + REM_INT, + REM_LONG, + REM_FLOAT, + REM_DOUBLE); + } + + /** + * Returns the appropriate {@code and} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opAnd(TypeList types) { + return pickBinaryOp(types, + AND_CONST_INT, + AND_CONST_LONG, + null, + null, + AND_INT, + AND_LONG, + null, + null); + } + + /** + * Returns the appropriate {@code or} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opOr(TypeList types) { + return pickBinaryOp(types, + OR_CONST_INT, + OR_CONST_LONG, + null, + null, + OR_INT, + OR_LONG, + null, + null); + } + + /** + * Returns the appropriate {@code xor} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opXor(TypeList types) { + return pickBinaryOp(types, + XOR_CONST_INT, + XOR_CONST_LONG, + null, + null, + XOR_INT, + XOR_LONG, + null, + null); + } + + /** + * Returns the appropriate {@code shl} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opShl(TypeList types) { + return pickBinaryOp(types, + SHL_CONST_INT, + SHL_CONST_LONG, + null, + null, + SHL_INT, + SHL_LONG, + null, + null); + } + + /** + * Returns the appropriate {@code shr} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opShr(TypeList types) { + return pickBinaryOp(types, + SHR_CONST_INT, + SHR_CONST_LONG, + null, + null, + SHR_INT, + SHR_LONG, + null, + null); + } + + /** + * Returns the appropriate {@code ushr} rop for the given + * types. The result is a shared instance. + * + * @param types {@code non-null;} types of the sources + * @return {@code non-null;} an appropriate instance + */ + public static Rop opUshr(TypeList types) { + return pickBinaryOp(types, + USHR_CONST_INT, + USHR_CONST_LONG, + null, + null, + USHR_INT, + USHR_LONG, + null, + null); + } + + /** + * Returns the appropriate binary arithmetic rop for the given type + * and arguments. The result is a shared instance. + * + * @param types {@code non-null;} sources of the operation + * @param int1 {@code non-null;} the int-to-constant rop + * @param long1 {@code non-null;} the long-to-constant rop + * @param float1 {@code null-ok;} the float-to-constant rop, if any + * @param double1 {@code null-ok;} the double-to-constant rop, if any + * @param int2 {@code non-null;} the int-to-int rop + * @param long2 {@code non-null;} the long-to-long or long-to-int rop + * @param float2 {@code null-ok;} the float-to-float rop, if any + * @param double2 {@code null-ok;} the double-to-double rop, if any + * @return {@code non-null;} an appropriate instance + */ + private static Rop pickBinaryOp(TypeList types, + Rop int1, + Rop long1, + Rop float1, + Rop double1, + Rop int2, + Rop long2, + Rop float2, + Rop double2) { + int bt1 = types.getType(0).getBasicFrameType(); + Rop result = null; + + switch (types.size()) { + case 1: { + switch (bt1) { + case Type.BT_INT: + return int1; + case Type.BT_LONG: + return long1; + case Type.BT_FLOAT: + result = float1; + break; + case Type.BT_DOUBLE: + result = double1; + break; } - - StdTypeList sourceTypes = new StdTypeList(count); - - for (int i = 0; i < count; i++) { - sourceTypes.set(i, elementType); + break; + } + case 2: { + switch (bt1) { + case Type.BT_INT: + return int2; + case Type.BT_LONG: + return long2; + case Type.BT_FLOAT: + result = float2; + break; + case Type.BT_DOUBLE: + result = double2; + break; } + break; + } + } - // Note: The resulting rop is considered call-like. - return new Rop(RegOps.FILLED_NEW_ARRAY, - sourceTypes, - Exceptions.LIST_Error); + if (result == null) { + return throwBadTypes(types); } - /** - * Returns the appropriate {@code get-field} rop for the given - * type. The result is a shared instance. - * - * @param type {@code non-null;} type of the field in question - * @return {@code non-null;} an appropriate instance - */ - public static Rop opGetField(TypeBearer type) { - switch (type.getBasicType()) { - case Type.BT_INT: return GET_FIELD_INT; - case Type.BT_LONG: return GET_FIELD_LONG; - case Type.BT_FLOAT: return GET_FIELD_FLOAT; - case Type.BT_DOUBLE: return GET_FIELD_DOUBLE; - case Type.BT_OBJECT: return GET_FIELD_OBJECT; - case Type.BT_BOOLEAN: return GET_FIELD_BOOLEAN; - case Type.BT_BYTE: return GET_FIELD_BYTE; - case Type.BT_CHAR: return GET_FIELD_CHAR; - case Type.BT_SHORT: return GET_FIELD_SHORT; - } + return result; + } + + /** + * Returns the appropriate {@code neg} rop for the given type. The + * result is a shared instance. + * + * @param type {@code non-null;} type of value being operated on + * @return {@code non-null;} an appropriate instance + */ + public static Rop opNeg(TypeBearer type) { + switch (type.getBasicFrameType()) { + case Type.BT_INT: + return NEG_INT; + case Type.BT_LONG: + return NEG_LONG; + case Type.BT_FLOAT: + return NEG_FLOAT; + case Type.BT_DOUBLE: + return NEG_DOUBLE; + } - return throwBadType(type); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code not} rop for the given type. The + * result is a shared instance. + * + * @param type {@code non-null;} type of value being operated on + * @return {@code non-null;} an appropriate instance + */ + public static Rop opNot(TypeBearer type) { + switch (type.getBasicFrameType()) { + case Type.BT_INT: + return NOT_INT; + case Type.BT_LONG: + return NOT_LONG; } - /** - * Returns the appropriate {@code put-field} rop for the given - * type. The result is a shared instance. - * - * @param type {@code non-null;} type of the field in question - * @return {@code non-null;} an appropriate instance - */ - public static Rop opPutField(TypeBearer type) { - switch (type.getBasicType()) { - case Type.BT_INT: return PUT_FIELD_INT; - case Type.BT_LONG: return PUT_FIELD_LONG; - case Type.BT_FLOAT: return PUT_FIELD_FLOAT; - case Type.BT_DOUBLE: return PUT_FIELD_DOUBLE; - case Type.BT_OBJECT: return PUT_FIELD_OBJECT; - case Type.BT_BOOLEAN: return PUT_FIELD_BOOLEAN; - case Type.BT_BYTE: return PUT_FIELD_BYTE; - case Type.BT_CHAR: return PUT_FIELD_CHAR; - case Type.BT_SHORT: return PUT_FIELD_SHORT; - } + return throwBadType(type); + } + + /** + * Returns the appropriate {@code cmpl} rop for the given type. The + * result is a shared instance. + * + * @param type {@code non-null;} type of value being compared + * @return {@code non-null;} an appropriate instance + */ + public static Rop opCmpl(TypeBearer type) { + switch (type.getBasicType()) { + case Type.BT_LONG: + return CMPL_LONG; + case Type.BT_FLOAT: + return CMPL_FLOAT; + case Type.BT_DOUBLE: + return CMPL_DOUBLE; + } - return throwBadType(type); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code cmpg} rop for the given type. The + * result is a shared instance. + * + * @param type {@code non-null;} type of value being compared + * @return {@code non-null;} an appropriate instance + */ + public static Rop opCmpg(TypeBearer type) { + switch (type.getBasicType()) { + case Type.BT_FLOAT: + return CMPG_FLOAT; + case Type.BT_DOUBLE: + return CMPG_DOUBLE; } - /** - * Returns the appropriate {@code get-static} rop for the given - * type. The result is a shared instance. - * - * @param type {@code non-null;} type of the field in question - * @return {@code non-null;} an appropriate instance - */ - public static Rop opGetStatic(TypeBearer type) { - switch (type.getBasicType()) { - case Type.BT_INT: return GET_STATIC_INT; - case Type.BT_LONG: return GET_STATIC_LONG; - case Type.BT_FLOAT: return GET_STATIC_FLOAT; - case Type.BT_DOUBLE: return GET_STATIC_DOUBLE; - case Type.BT_OBJECT: return GET_STATIC_OBJECT; - case Type.BT_BOOLEAN: return GET_STATIC_BOOLEAN; - case Type.BT_BYTE: return GET_STATIC_BYTE; - case Type.BT_CHAR: return GET_STATIC_CHAR; - case Type.BT_SHORT: return GET_STATIC_SHORT; + return throwBadType(type); + } + + /** + * Returns the appropriate {@code conv} rop for the given types. The + * result is a shared instance. + * + * @param dest {@code non-null;} target value type + * @param source {@code non-null;} source value type + * @return {@code non-null;} an appropriate instance + */ + public static Rop opConv(TypeBearer dest, TypeBearer source) { + int dbt = dest.getBasicFrameType(); + switch (source.getBasicFrameType()) { + case Type.BT_INT: { + switch (dbt) { + case Type.BT_LONG: + return CONV_I2L; + case Type.BT_FLOAT: + return CONV_I2F; + case Type.BT_DOUBLE: + return CONV_I2D; + default: + break; } - - return throwBadType(type); + break; + } + case Type.BT_LONG: { + switch (dbt) { + case Type.BT_INT: + return CONV_L2I; + case Type.BT_FLOAT: + return CONV_L2F; + case Type.BT_DOUBLE: + return CONV_L2D; + default: + break; + } + break; + } + case Type.BT_FLOAT: { + switch (dbt) { + case Type.BT_INT: + return CONV_F2I; + case Type.BT_LONG: + return CONV_F2L; + case Type.BT_DOUBLE: + return CONV_F2D; + default: + break; + } + break; + } + case Type.BT_DOUBLE: { + switch (dbt) { + case Type.BT_INT: + return CONV_D2I; + case Type.BT_LONG: + return CONV_D2L; + case Type.BT_FLOAT: + return CONV_D2F; + default: + break; + } + } } - /** - * Returns the appropriate {@code put-static} rop for the given - * type. The result is a shared instance. - * - * @param type {@code non-null;} type of the field in question - * @return {@code non-null;} an appropriate instance - */ - public static Rop opPutStatic(TypeBearer type) { - switch (type.getBasicType()) { - case Type.BT_INT: return PUT_STATIC_INT; - case Type.BT_LONG: return PUT_STATIC_LONG; - case Type.BT_FLOAT: return PUT_STATIC_FLOAT; - case Type.BT_DOUBLE: return PUT_STATIC_DOUBLE; - case Type.BT_OBJECT: return PUT_STATIC_OBJECT; - case Type.BT_BOOLEAN: return PUT_STATIC_BOOLEAN; - case Type.BT_BYTE: return PUT_STATIC_BYTE; - case Type.BT_CHAR: return PUT_STATIC_CHAR; - case Type.BT_SHORT: return PUT_STATIC_SHORT; - } + return throwBadTypes(StdTypeList.make(dest.getType(), source.getType())); + } + + /** + * Returns the appropriate {@code return} rop for the given type. The + * result is a shared instance. + * + * @param type {@code non-null;} type of value being returned + * @return {@code non-null;} an appropriate instance + */ + public static Rop opReturn(TypeBearer type) { + switch (type.getBasicFrameType()) { + case Type.BT_INT: + return RETURN_INT; + case Type.BT_LONG: + return RETURN_LONG; + case Type.BT_FLOAT: + return RETURN_FLOAT; + case Type.BT_DOUBLE: + return RETURN_DOUBLE; + case Type.BT_OBJECT: + return RETURN_OBJECT; + case Type.BT_VOID: + return RETURN_VOID; + } - return throwBadType(type); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code aget} rop for the given type. The + * result is a shared instance. + * + * @param type {@code non-null;} element type of array being accessed + * @return {@code non-null;} an appropriate instance + */ + public static Rop opAget(TypeBearer type) { + switch (type.getBasicType()) { + case Type.BT_INT: + return AGET_INT; + case Type.BT_LONG: + return AGET_LONG; + case Type.BT_FLOAT: + return AGET_FLOAT; + case Type.BT_DOUBLE: + return AGET_DOUBLE; + case Type.BT_OBJECT: + return AGET_OBJECT; + case Type.BT_BOOLEAN: + return AGET_BOOLEAN; + case Type.BT_BYTE: + return AGET_BYTE; + case Type.BT_CHAR: + return AGET_CHAR; + case Type.BT_SHORT: + return AGET_SHORT; } - /** - * Returns the appropriate {@code invoke-static} rop for the - * given type. The result is typically a newly-allocated instance. - * - * @param meth {@code non-null;} descriptor of the method - * @return {@code non-null;} an appropriate instance - */ - public static Rop opInvokeStatic(Prototype meth) { - return new Rop(RegOps.INVOKE_STATIC, - meth.getParameterFrameTypes(), - StdTypeList.THROWABLE); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code aput} rop for the given type. The + * result is a shared instance. + * + * @param type {@code non-null;} element type of array being accessed + * @return {@code non-null;} an appropriate instance + */ + public static Rop opAput(TypeBearer type) { + switch (type.getBasicType()) { + case Type.BT_INT: + return APUT_INT; + case Type.BT_LONG: + return APUT_LONG; + case Type.BT_FLOAT: + return APUT_FLOAT; + case Type.BT_DOUBLE: + return APUT_DOUBLE; + case Type.BT_OBJECT: + return APUT_OBJECT; + case Type.BT_BOOLEAN: + return APUT_BOOLEAN; + case Type.BT_BYTE: + return APUT_BYTE; + case Type.BT_CHAR: + return APUT_CHAR; + case Type.BT_SHORT: + return APUT_SHORT; } - /** - * Returns the appropriate {@code invoke-virtual} rop for the - * given type. The result is typically a newly-allocated instance. - * - * @param meth {@code non-null;} descriptor of the method, including the - * {@code this} parameter - * @return {@code non-null;} an appropriate instance - */ - public static Rop opInvokeVirtual(Prototype meth) { - return new Rop(RegOps.INVOKE_VIRTUAL, - meth.getParameterFrameTypes(), - StdTypeList.THROWABLE); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code new-array} rop for the given + * type. The result is a shared instance. + * + * @param arrayType {@code non-null;} array type of array being created + * @return {@code non-null;} an appropriate instance + */ + public static Rop opNewArray(TypeBearer arrayType) { + Type type = arrayType.getType(); + Type elementType = type.getComponentType(); + + switch (elementType.getBasicType()) { + case Type.BT_INT: + return NEW_ARRAY_INT; + case Type.BT_LONG: + return NEW_ARRAY_LONG; + case Type.BT_FLOAT: + return NEW_ARRAY_FLOAT; + case Type.BT_DOUBLE: + return NEW_ARRAY_DOUBLE; + case Type.BT_BOOLEAN: + return NEW_ARRAY_BOOLEAN; + case Type.BT_BYTE: + return NEW_ARRAY_BYTE; + case Type.BT_CHAR: + return NEW_ARRAY_CHAR; + case Type.BT_SHORT: + return NEW_ARRAY_SHORT; + case Type.BT_OBJECT: { + return new Rop(RegOps.NEW_ARRAY, type, StdTypeList.INT, + Exceptions.LIST_Error_NegativeArraySizeException, "new-array-object"); + } } - /** - * Returns the appropriate {@code invoke-super} rop for the - * given type. The result is typically a newly-allocated instance. - * - * @param meth {@code non-null;} descriptor of the method, including the - * {@code this} parameter - * @return {@code non-null;} an appropriate instance - */ - public static Rop opInvokeSuper(Prototype meth) { - return new Rop(RegOps.INVOKE_SUPER, - meth.getParameterFrameTypes(), - StdTypeList.THROWABLE); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code filled-new-array} rop for the given + * type. The result may be a shared instance. + * + * @param arrayType {@code non-null;} type of array being created + * @param count {@code >= 0;} number of elements that the array should have + * @return {@code non-null;} an appropriate instance + */ + public static Rop opFilledNewArray(TypeBearer arrayType, int count) { + Type type = arrayType.getType(); + Type elementType = type.getComponentType(); + + if (elementType.isCategory2()) { + return throwBadType(arrayType); } - /** - * Returns the appropriate {@code invoke-direct} rop for the - * given type. The result is typically a newly-allocated instance. - * - * @param meth {@code non-null;} descriptor of the method, including the - * {@code this} parameter - * @return {@code non-null;} an appropriate instance - */ - public static Rop opInvokeDirect(Prototype meth) { - return new Rop(RegOps.INVOKE_DIRECT, - meth.getParameterFrameTypes(), - StdTypeList.THROWABLE); + if (count < 0) { + throw new IllegalArgumentException("count < 0"); } - /** - * Returns the appropriate {@code invoke-interface} rop for the - * given type. The result is typically a newly-allocated instance. - * - * @param meth {@code non-null;} descriptor of the method, including the - * {@code this} parameter - * @return {@code non-null;} an appropriate instance - */ - public static Rop opInvokeInterface(Prototype meth) { - return new Rop(RegOps.INVOKE_INTERFACE, - meth.getParameterFrameTypes(), - StdTypeList.THROWABLE); + StdTypeList sourceTypes = new StdTypeList(count); + + for (int i = 0; i < count; i++) { + sourceTypes.set(i, elementType); } - /** - * Returns the appropriate {@code mark-local} rop for the given type. - * The result is a shared instance. - * - * @param type {@code non-null;} type of value being marked - * @return {@code non-null;} an appropriate instance - */ - public static Rop opMarkLocal(TypeBearer type) { - switch (type.getBasicFrameType()) { - case Type.BT_INT: return MARK_LOCAL_INT; - case Type.BT_LONG: return MARK_LOCAL_LONG; - case Type.BT_FLOAT: return MARK_LOCAL_FLOAT; - case Type.BT_DOUBLE: return MARK_LOCAL_DOUBLE; - case Type.BT_OBJECT: return MARK_LOCAL_OBJECT; - } + // Note: The resulting rop is considered call-like. + return new Rop(RegOps.FILLED_NEW_ARRAY, sourceTypes, Exceptions.LIST_Error); + } + + /** + * Returns the appropriate {@code get-field} rop for the given + * type. The result is a shared instance. + * + * @param type {@code non-null;} type of the field in question + * @return {@code non-null;} an appropriate instance + */ + public static Rop opGetField(TypeBearer type) { + switch (type.getBasicType()) { + case Type.BT_INT: + return GET_FIELD_INT; + case Type.BT_LONG: + return GET_FIELD_LONG; + case Type.BT_FLOAT: + return GET_FIELD_FLOAT; + case Type.BT_DOUBLE: + return GET_FIELD_DOUBLE; + case Type.BT_OBJECT: + return GET_FIELD_OBJECT; + case Type.BT_BOOLEAN: + return GET_FIELD_BOOLEAN; + case Type.BT_BYTE: + return GET_FIELD_BYTE; + case Type.BT_CHAR: + return GET_FIELD_CHAR; + case Type.BT_SHORT: + return GET_FIELD_SHORT; + } - return throwBadType(type); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code put-field} rop for the given + * type. The result is a shared instance. + * + * @param type {@code non-null;} type of the field in question + * @return {@code non-null;} an appropriate instance + */ + public static Rop opPutField(TypeBearer type) { + switch (type.getBasicType()) { + case Type.BT_INT: + return PUT_FIELD_INT; + case Type.BT_LONG: + return PUT_FIELD_LONG; + case Type.BT_FLOAT: + return PUT_FIELD_FLOAT; + case Type.BT_DOUBLE: + return PUT_FIELD_DOUBLE; + case Type.BT_OBJECT: + return PUT_FIELD_OBJECT; + case Type.BT_BOOLEAN: + return PUT_FIELD_BOOLEAN; + case Type.BT_BYTE: + return PUT_FIELD_BYTE; + case Type.BT_CHAR: + return PUT_FIELD_CHAR; + case Type.BT_SHORT: + return PUT_FIELD_SHORT; } - /** - * This class is uninstantiable. - */ - private Rops() { - // This space intentionally left blank. + return throwBadType(type); + } + + /** + * Returns the appropriate {@code get-static} rop for the given + * type. The result is a shared instance. + * + * @param type {@code non-null;} type of the field in question + * @return {@code non-null;} an appropriate instance + */ + public static Rop opGetStatic(TypeBearer type) { + switch (type.getBasicType()) { + case Type.BT_INT: + return GET_STATIC_INT; + case Type.BT_LONG: + return GET_STATIC_LONG; + case Type.BT_FLOAT: + return GET_STATIC_FLOAT; + case Type.BT_DOUBLE: + return GET_STATIC_DOUBLE; + case Type.BT_OBJECT: + return GET_STATIC_OBJECT; + case Type.BT_BOOLEAN: + return GET_STATIC_BOOLEAN; + case Type.BT_BYTE: + return GET_STATIC_BYTE; + case Type.BT_CHAR: + return GET_STATIC_CHAR; + case Type.BT_SHORT: + return GET_STATIC_SHORT; } - /** - * Throws the right exception to complain about a bogus type. - * - * @param type {@code non-null;} the bad type - * @return never - */ - private static Rop throwBadType(TypeBearer type) { - throw new IllegalArgumentException("bad type: " + type); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code put-static} rop for the given + * type. The result is a shared instance. + * + * @param type {@code non-null;} type of the field in question + * @return {@code non-null;} an appropriate instance + */ + public static Rop opPutStatic(TypeBearer type) { + switch (type.getBasicType()) { + case Type.BT_INT: + return PUT_STATIC_INT; + case Type.BT_LONG: + return PUT_STATIC_LONG; + case Type.BT_FLOAT: + return PUT_STATIC_FLOAT; + case Type.BT_DOUBLE: + return PUT_STATIC_DOUBLE; + case Type.BT_OBJECT: + return PUT_STATIC_OBJECT; + case Type.BT_BOOLEAN: + return PUT_STATIC_BOOLEAN; + case Type.BT_BYTE: + return PUT_STATIC_BYTE; + case Type.BT_CHAR: + return PUT_STATIC_CHAR; + case Type.BT_SHORT: + return PUT_STATIC_SHORT; } - /** - * Throws the right exception to complain about a bogus list of types. - * - * @param types {@code non-null;} the bad types - * @return never - */ - private static Rop throwBadTypes(TypeList types) { - throw new IllegalArgumentException("bad types: " + types); + return throwBadType(type); + } + + /** + * Returns the appropriate {@code invoke-static} rop for the + * given type. The result is typically a newly-allocated instance. + * + * @param meth {@code non-null;} descriptor of the method + * @return {@code non-null;} an appropriate instance + */ + public static Rop opInvokeStatic(Prototype meth) { + return new Rop(RegOps.INVOKE_STATIC, meth.getParameterFrameTypes(), StdTypeList.THROWABLE); + } + + /** + * Returns the appropriate {@code invoke-virtual} rop for the + * given type. The result is typically a newly-allocated instance. + * + * @param meth {@code non-null;} descriptor of the method, including the + * {@code this} parameter + * @return {@code non-null;} an appropriate instance + */ + public static Rop opInvokeVirtual(Prototype meth) { + return new Rop(RegOps.INVOKE_VIRTUAL, meth.getParameterFrameTypes(), StdTypeList.THROWABLE); + } + + /** + * Returns the appropriate {@code invoke-super} rop for the + * given type. The result is typically a newly-allocated instance. + * + * @param meth {@code non-null;} descriptor of the method, including the + * {@code this} parameter + * @return {@code non-null;} an appropriate instance + */ + public static Rop opInvokeSuper(Prototype meth) { + return new Rop(RegOps.INVOKE_SUPER, meth.getParameterFrameTypes(), StdTypeList.THROWABLE); + } + + /** + * Returns the appropriate {@code invoke-direct} rop for the + * given type. The result is typically a newly-allocated instance. + * + * @param meth {@code non-null;} descriptor of the method, including the + * {@code this} parameter + * @return {@code non-null;} an appropriate instance + */ + public static Rop opInvokeDirect(Prototype meth) { + return new Rop(RegOps.INVOKE_DIRECT, meth.getParameterFrameTypes(), StdTypeList.THROWABLE); + } + + /** + * Returns the appropriate {@code invoke-interface} rop for the + * given type. The result is typically a newly-allocated instance. + * + * @param meth {@code non-null;} descriptor of the method, including the + * {@code this} parameter + * @return {@code non-null;} an appropriate instance + */ + public static Rop opInvokeInterface(Prototype meth) { + return new Rop(RegOps.INVOKE_INTERFACE, meth.getParameterFrameTypes(), StdTypeList.THROWABLE); + } + + /** + * Returns the appropriate {@code mark-local} rop for the given type. + * The result is a shared instance. + * + * @param type {@code non-null;} type of value being marked + * @return {@code non-null;} an appropriate instance + */ + public static Rop opMarkLocal(TypeBearer type) { + switch (type.getBasicFrameType()) { + case Type.BT_INT: + return MARK_LOCAL_INT; + case Type.BT_LONG: + return MARK_LOCAL_LONG; + case Type.BT_FLOAT: + return MARK_LOCAL_FLOAT; + case Type.BT_DOUBLE: + return MARK_LOCAL_DOUBLE; + case Type.BT_OBJECT: + return MARK_LOCAL_OBJECT; } + + return throwBadType(type); + } + + /** + * This class is uninstantiable. + */ + private Rops() { + // This space intentionally left blank. + } + + /** + * Throws the right exception to complain about a bogus type. + * + * @param type {@code non-null;} the bad type + * @return never + */ + private static Rop throwBadType(TypeBearer type) { + throw new IllegalArgumentException("bad type: " + type); + } + + /** + * Throws the right exception to complain about a bogus list of types. + * + * @param types {@code non-null;} the bad types + * @return never + */ + private static Rop throwBadTypes(TypeList types) { + throw new IllegalArgumentException("bad types: " + types); + } } diff --git a/dx/src/com/android/jack/dx/rop/code/SourcePosition.java b/dx/src/com/android/jack/dx/rop/code/SourcePosition.java index 0c4c122..0aeea9a 100644 --- a/dx/src/com/android/jack/dx/rop/code/SourcePosition.java +++ b/dx/src/com/android/jack/dx/rop/code/SourcePosition.java @@ -24,145 +24,143 @@ import com.android.jack.dx.util.Hex; * line number and original bytecode address. */ public final class SourcePosition { - /** {@code non-null;} convenient "no information known" instance */ - public static final SourcePosition NO_INFO = - new SourcePosition(null, -1, -1); - - /** {@code null-ok;} name of the file of origin or {@code null} if unknown */ - private final CstString sourceFile; - - /** - * {@code >= -1;} the bytecode address, or {@code -1} if that - * information is unknown - */ - private final int address; - - /** - * {@code >= -1;} the line number, or {@code -1} if that - * information is unknown - */ - private final int line; - - /** - * Constructs an instance. - * - * @param sourceFile {@code null-ok;} name of the file of origin or - * {@code null} if unknown - * @param address {@code >= -1;} original bytecode address or {@code -1} - * if unknown - * @param line {@code >= -1;} original line number or {@code -1} if - * unknown - */ - public SourcePosition(CstString sourceFile, int address, int line) { - if (address < -1) { - throw new IllegalArgumentException("address < -1"); - } - - if (line < -1) { - throw new IllegalArgumentException("line < -1"); - } - - this.sourceFile = sourceFile; - this.address = address; - this.line = line; + /** {@code non-null;} convenient "no information known" instance */ + public static final SourcePosition NO_INFO = new SourcePosition(null, -1, -1); + + /** {@code null-ok;} name of the file of origin or {@code null} if unknown */ + private final CstString sourceFile; + + /** + * {@code >= -1;} the bytecode address, or {@code -1} if that + * information is unknown + */ + private final int address; + + /** + * {@code >= -1;} the line number, or {@code -1} if that + * information is unknown + */ + private final int line; + + /** + * Constructs an instance. + * + * @param sourceFile {@code null-ok;} name of the file of origin or + * {@code null} if unknown + * @param address {@code >= -1;} original bytecode address or {@code -1} + * if unknown + * @param line {@code >= -1;} original line number or {@code -1} if + * unknown + */ + public SourcePosition(CstString sourceFile, int address, int line) { + if (address < -1) { + throw new IllegalArgumentException("address < -1"); } - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(50); - - if (sourceFile != null) { - sb.append(sourceFile.toHuman()); - sb.append(":"); - } - - if (line >= 0) { - sb.append(line); - } - - sb.append('@'); - - if (address < 0) { - sb.append("????"); - } else { - sb.append(Hex.u2(address)); - } - - return sb.toString(); + if (line < -1) { + throw new IllegalArgumentException("line < -1"); } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof SourcePosition)) { - return false; - } + this.sourceFile = sourceFile; + this.address = address; + this.line = line; + } - if (this == other) { - return true; - } + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(50); - SourcePosition pos = (SourcePosition) other; - - return (address == pos.address) && sameLineAndFile(pos); + if (sourceFile != null) { + sb.append(sourceFile.toHuman()); + sb.append(":"); } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return sourceFile.hashCode() + address + line; + if (line >= 0) { + sb.append(line); } - /** - * Returns whether the lines match between this instance and - * the one given. - * - * @param other {@code non-null;} the instance to compare to - * @return {@code true} iff the lines match - */ - public boolean sameLine(SourcePosition other) { - return (line == other.line); - } + sb.append('@'); - /** - * Returns whether the lines and files match between this instance and - * the one given. - * - * @param other {@code non-null;} the instance to compare to - * @return {@code true} iff the lines and files match - */ - public boolean sameLineAndFile(SourcePosition other) { - return (line == other.line) && - ((sourceFile == other.sourceFile) || - ((sourceFile != null) && sourceFile.equals(other.sourceFile))); + if (address < 0) { + sb.append("????"); + } else { + sb.append(Hex.u2(address)); } - /** - * Gets the source file, if known. - * - * @return {@code null-ok;} the source file or {@code null} if unknown - */ - public CstString getSourceFile() { - return sourceFile; - } + return sb.toString(); + } - /** - * Gets the original bytecode address. - * - * @return {@code >= -1;} the address or {@code -1} if unknown - */ - public int getAddress() { - return address; + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof SourcePosition)) { + return false; } - /** - * Gets the original line number. - * - * @return {@code >= -1;} the original line number or {@code -1} if - * unknown - */ - public int getLine() { - return line; + if (this == other) { + return true; } + + SourcePosition pos = (SourcePosition) other; + + return (address == pos.address) && sameLineAndFile(pos); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return sourceFile.hashCode() + address + line; + } + + /** + * Returns whether the lines match between this instance and + * the one given. + * + * @param other {@code non-null;} the instance to compare to + * @return {@code true} iff the lines match + */ + public boolean sameLine(SourcePosition other) { + return (line == other.line); + } + + /** + * Returns whether the lines and files match between this instance and + * the one given. + * + * @param other {@code non-null;} the instance to compare to + * @return {@code true} iff the lines and files match + */ + public boolean sameLineAndFile(SourcePosition other) { + return (line == other.line) && ((sourceFile == other.sourceFile) + || ((sourceFile != null) && sourceFile.equals(other.sourceFile))); + } + + /** + * Gets the source file, if known. + * + * @return {@code null-ok;} the source file or {@code null} if unknown + */ + public CstString getSourceFile() { + return sourceFile; + } + + /** + * Gets the original bytecode address. + * + * @return {@code >= -1;} the address or {@code -1} if unknown + */ + public int getAddress() { + return address; + } + + /** + * Gets the original line number. + * + * @return {@code >= -1;} the original line number or {@code -1} if + * unknown + */ + public int getLine() { + return line; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/SwitchInsn.java b/dx/src/com/android/jack/dx/rop/code/SwitchInsn.java index a91b624..81e286b 100644 --- a/dx/src/com/android/jack/dx/rop/code/SwitchInsn.java +++ b/dx/src/com/android/jack/dx/rop/code/SwitchInsn.java @@ -24,96 +24,89 @@ import com.android.jack.dx.util.IntList; /** * Instruction which contains switch cases. */ -public final class SwitchInsn - extends Insn { - /** {@code non-null;} list of switch cases */ - private final IntList cases; - - /** - * Constructs an instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param result {@code null-ok;} spec for the result, if any - * @param sources {@code non-null;} specs for all the sources - * @param cases {@code non-null;} list of switch cases - */ - public SwitchInsn(Rop opcode, SourcePosition position, RegisterSpec result, - RegisterSpecList sources, IntList cases) { - super(opcode, position, result, sources); - - if (opcode.getBranchingness() != Rop.BRANCH_SWITCH) { - throw new IllegalArgumentException("bogus branchingness"); - } - - if (cases == null) { - throw new NullPointerException("cases == null"); - } - - this.cases = cases; +public final class SwitchInsn extends Insn { + /** {@code non-null;} list of switch cases */ + private final IntList cases; + + /** + * Constructs an instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param result {@code null-ok;} spec for the result, if any + * @param sources {@code non-null;} specs for all the sources + * @param cases {@code non-null;} list of switch cases + */ + public SwitchInsn(Rop opcode, SourcePosition position, RegisterSpec result, + RegisterSpecList sources, IntList cases) { + super(opcode, position, result, sources); + + if (opcode.getBranchingness() != Rop.BRANCH_SWITCH) { + throw new IllegalArgumentException("bogus branchingness"); } - /** {@inheritDoc} */ - @Override - public String getInlineString() { - return cases.toString(); + if (cases == null) { + throw new NullPointerException("cases == null"); } - /** {@inheritDoc} */ - @Override - public TypeList getCatches() { - return StdTypeList.EMPTY; - } - - /** {@inheritDoc} */ - @Override - public void accept(Visitor visitor) { - visitor.visitSwitchInsn(this); - } - - /** {@inheritDoc} */ - @Override - public Insn withAddedCatch(Type type) { - throw new UnsupportedOperationException("unsupported"); - } - - /** {@inheritDoc} */ - @Override - public Insn withRegisterOffset(int delta) { - return new SwitchInsn(getOpcode(), getPosition(), - getResult().withOffset(delta), - getSources().withOffset(delta), - cases); - } - - /** - * {@inheritDoc} - * - * <p> SwitchInsn always compares false. The current use for this method - * never encounters {@code SwitchInsn}s - */ - @Override - public boolean contentEquals(Insn b) { - return false; - } - - /** {@inheritDoc} */ - @Override - public Insn withNewRegisters(RegisterSpec result, - RegisterSpecList sources) { - - return new SwitchInsn(getOpcode(), getPosition(), - result, - sources, - cases); - } - - /** - * Gets the list of switch cases. - * - * @return {@code non-null;} the case list - */ - public IntList getCases() { - return cases; - } + this.cases = cases; + } + + /** {@inheritDoc} */ + @Override + public String getInlineString() { + return cases.toString(); + } + + /** {@inheritDoc} */ + @Override + public TypeList getCatches() { + return StdTypeList.EMPTY; + } + + /** {@inheritDoc} */ + @Override + public void accept(Visitor visitor) { + visitor.visitSwitchInsn(this); + } + + /** {@inheritDoc} */ + @Override + public Insn withAddedCatch(Type type) { + throw new UnsupportedOperationException("unsupported"); + } + + /** {@inheritDoc} */ + @Override + public Insn withRegisterOffset(int delta) { + return new SwitchInsn(getOpcode(), getPosition(), getResult().withOffset(delta), + getSources().withOffset(delta), cases); + } + + /** + * {@inheritDoc} + * + * <p> SwitchInsn always compares false. The current use for this method + * never encounters {@code SwitchInsn}s + */ + @Override + public boolean contentEquals(Insn b) { + return false; + } + + /** {@inheritDoc} */ + @Override + public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) { + + return new SwitchInsn(getOpcode(), getPosition(), result, sources, cases); + } + + /** + * Gets the list of switch cases. + * + * @return {@code non-null;} the case list + */ + public IntList getCases() { + return cases; + } } diff --git a/dx/src/com/android/jack/dx/rop/code/ThrowingCstInsn.java b/dx/src/com/android/jack/dx/rop/code/ThrowingCstInsn.java index aad2a03..b8c93e8 100644 --- a/dx/src/com/android/jack/dx/rop/code/ThrowingCstInsn.java +++ b/dx/src/com/android/jack/dx/rop/code/ThrowingCstInsn.java @@ -25,86 +25,77 @@ import com.android.jack.dx.rop.type.TypeList; * Instruction which contains an explicit reference to a constant * and which might throw an exception. */ -public final class ThrowingCstInsn - extends CstInsn { - /** {@code non-null;} list of exceptions caught */ - private final TypeList catches; - - /** - * Constructs an instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param sources {@code non-null;} specs for all the sources - * @param catches {@code non-null;} list of exceptions caught - * @param cst {@code non-null;} the constant - */ - public ThrowingCstInsn(Rop opcode, SourcePosition position, - RegisterSpecList sources, - TypeList catches, Constant cst) { - super(opcode, position, null, sources, cst); - - if (opcode.getBranchingness() != Rop.BRANCH_THROW) { - throw new IllegalArgumentException("bogus branchingness"); - } - - if (catches == null) { - throw new NullPointerException("catches == null"); - } - - this.catches = catches; +public final class ThrowingCstInsn extends CstInsn { + /** {@code non-null;} list of exceptions caught */ + private final TypeList catches; + + /** + * Constructs an instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param sources {@code non-null;} specs for all the sources + * @param catches {@code non-null;} list of exceptions caught + * @param cst {@code non-null;} the constant + */ + public ThrowingCstInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, + TypeList catches, Constant cst) { + super(opcode, position, null, sources, cst); + + if (opcode.getBranchingness() != Rop.BRANCH_THROW) { + throw new IllegalArgumentException("bogus branchingness"); } - /** {@inheritDoc} */ - @Override - public String getInlineString() { - Constant cst = getConstant(); - String constantString = cst.toHuman(); - if (cst instanceof CstString) { - constantString = ((CstString) cst).toQuoted(); - } - return constantString + " " + ThrowingInsn.toCatchString(catches); + if (catches == null) { + throw new NullPointerException("catches == null"); } - /** {@inheritDoc} */ - @Override - public TypeList getCatches() { - return catches; - } - - /** {@inheritDoc} */ - @Override - public void accept(Visitor visitor) { - visitor.visitThrowingCstInsn(this); - } - - /** {@inheritDoc} */ - @Override - public Insn withAddedCatch(Type type) { - return new ThrowingCstInsn(getOpcode(), getPosition(), - getSources(), catches.withAddedType(type), - getConstant()); - } - - /** {@inheritDoc} */ - @Override - public Insn withRegisterOffset(int delta) { - return new ThrowingCstInsn(getOpcode(), getPosition(), - getSources().withOffset(delta), - catches, - getConstant()); - } - - /** {@inheritDoc} */ - @Override - public Insn withNewRegisters(RegisterSpec result, - RegisterSpecList sources) { + this.catches = catches; + } - return new ThrowingCstInsn(getOpcode(), getPosition(), - sources, - catches, - getConstant()); + /** {@inheritDoc} */ + @Override + public String getInlineString() { + Constant cst = getConstant(); + String constantString = cst.toHuman(); + if (cst instanceof CstString) { + constantString = ((CstString) cst).toQuoted(); } + return constantString + " " + ThrowingInsn.toCatchString(catches); + } + + /** {@inheritDoc} */ + @Override + public TypeList getCatches() { + return catches; + } + + /** {@inheritDoc} */ + @Override + public void accept(Visitor visitor) { + visitor.visitThrowingCstInsn(this); + } + + /** {@inheritDoc} */ + @Override + public Insn withAddedCatch(Type type) { + return new ThrowingCstInsn(getOpcode(), getPosition(), getSources(), + catches.withAddedType(type), getConstant()); + } + + /** {@inheritDoc} */ + @Override + public Insn withRegisterOffset(int delta) { + return new ThrowingCstInsn(getOpcode(), getPosition(), getSources().withOffset(delta), catches, + getConstant()); + } + + /** {@inheritDoc} */ + @Override + public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) { + + return new ThrowingCstInsn(getOpcode(), getPosition(), sources, catches, getConstant()); + } } diff --git a/dx/src/com/android/jack/dx/rop/code/ThrowingInsn.java b/dx/src/com/android/jack/dx/rop/code/ThrowingInsn.java index cc12194..21956cf 100644 --- a/dx/src/com/android/jack/dx/rop/code/ThrowingInsn.java +++ b/dx/src/com/android/jack/dx/rop/code/ThrowingInsn.java @@ -25,96 +25,88 @@ import com.android.jack.dx.rop.type.TypeList; * the list of exceptions handled by this instruction, with the * no-exception case appended as the final target. */ -public final class ThrowingInsn - extends Insn { - /** {@code non-null;} list of exceptions caught */ - private final TypeList catches; - - /** - * Gets the string form of a register spec list to be used as a catches - * list. - * - * @param catches {@code non-null;} the catches list - * @return {@code non-null;} the string form - */ - public static String toCatchString(TypeList catches) { - StringBuffer sb = new StringBuffer(100); - - sb.append("catch"); - - int sz = catches.size(); - for (int i = 0; i < sz; i++) { - sb.append(" "); - sb.append(catches.getType(i).toHuman()); - } - - return sb.toString(); +public final class ThrowingInsn extends Insn { + /** {@code non-null;} list of exceptions caught */ + private final TypeList catches; + + /** + * Gets the string form of a register spec list to be used as a catches + * list. + * + * @param catches {@code non-null;} the catches list + * @return {@code non-null;} the string form + */ + public static String toCatchString(TypeList catches) { + StringBuffer sb = new StringBuffer(100); + + sb.append("catch"); + + int sz = catches.size(); + for (int i = 0; i < sz; i++) { + sb.append(" "); + sb.append(catches.getType(i).toHuman()); } - /** - * Constructs an instance. - * - * @param opcode {@code non-null;} the opcode - * @param position {@code non-null;} source position - * @param sources {@code non-null;} specs for all the sources - * @param catches {@code non-null;} list of exceptions caught - */ - public ThrowingInsn(Rop opcode, SourcePosition position, - RegisterSpecList sources, - TypeList catches) { - super(opcode, position, null, sources); - - if (opcode.getBranchingness() != Rop.BRANCH_THROW) { - throw new IllegalArgumentException("bogus branchingness"); - } - - if (catches == null) { - throw new NullPointerException("catches == null"); - } - - this.catches = catches; + return sb.toString(); + } + + /** + * Constructs an instance. + * + * @param opcode {@code non-null;} the opcode + * @param position {@code non-null;} source position + * @param sources {@code non-null;} specs for all the sources + * @param catches {@code non-null;} list of exceptions caught + */ + public ThrowingInsn(Rop opcode, SourcePosition position, RegisterSpecList sources, + TypeList catches) { + super(opcode, position, null, sources); + + if (opcode.getBranchingness() != Rop.BRANCH_THROW) { + throw new IllegalArgumentException("bogus branchingness"); } - /** {@inheritDoc} */ - @Override - public String getInlineString() { - return toCatchString(catches); + if (catches == null) { + throw new NullPointerException("catches == null"); } - /** {@inheritDoc} */ - @Override - public TypeList getCatches() { - return catches; - } - - /** {@inheritDoc} */ - @Override - public void accept(Visitor visitor) { - visitor.visitThrowingInsn(this); - } - - /** {@inheritDoc} */ - @Override - public Insn withAddedCatch(Type type) { - return new ThrowingInsn(getOpcode(), getPosition(), - getSources(), catches.withAddedType(type)); - } - - /** {@inheritDoc} */ - @Override - public Insn withRegisterOffset(int delta) { - return new ThrowingInsn(getOpcode(), getPosition(), - getSources().withOffset(delta), - catches); - } - - /** {@inheritDoc} */ - @Override - public Insn withNewRegisters(RegisterSpec result, - RegisterSpecList sources) { - - return new ThrowingInsn(getOpcode(), getPosition(), - sources, - catches); - } + this.catches = catches; + } + + /** {@inheritDoc} */ + @Override + public String getInlineString() { + return toCatchString(catches); + } + + /** {@inheritDoc} */ + @Override + public TypeList getCatches() { + return catches; + } + + /** {@inheritDoc} */ + @Override + public void accept(Visitor visitor) { + visitor.visitThrowingInsn(this); + } + + /** {@inheritDoc} */ + @Override + public Insn withAddedCatch(Type type) { + return new ThrowingInsn(getOpcode(), getPosition(), getSources(), catches.withAddedType(type)); + } + + /** {@inheritDoc} */ + @Override + public Insn withRegisterOffset(int delta) { + return new ThrowingInsn(getOpcode(), getPosition(), getSources().withOffset(delta), catches); + } + + /** {@inheritDoc} */ + @Override + public Insn withNewRegisters(RegisterSpec result, RegisterSpecList sources) { + + return new ThrowingInsn(getOpcode(), getPosition(), sources, catches); + } } diff --git a/dx/src/com/android/jack/dx/rop/code/TranslationAdvice.java b/dx/src/com/android/jack/dx/rop/code/TranslationAdvice.java index 32936ba..af78386 100644 --- a/dx/src/com/android/jack/dx/rop/code/TranslationAdvice.java +++ b/dx/src/com/android/jack/dx/rop/code/TranslationAdvice.java @@ -23,40 +23,39 @@ package com.android.jack.dx.rop.code; * the early stage code be explicitly tied to the target. */ public interface TranslationAdvice { - /** - * Returns an indication of whether the target can directly represent an - * instruction with the given opcode operating on the given arguments, - * where the last source argument is used as a constant. (That is, the - * last argument must have a type which indicates it is a known constant.) - * The instruction associated must have exactly two sources. - * - * @param opcode {@code non-null;} the opcode - * @param sourceA {@code non-null;} the first source - * @param sourceB {@code non-null;} the second source - * @return {@code true} iff the target can represent the operation - * using a constant for the last argument - */ - public boolean hasConstantOperation(Rop opcode, - RegisterSpec sourceA, RegisterSpec sourceB); + /** + * Returns an indication of whether the target can directly represent an + * instruction with the given opcode operating on the given arguments, + * where the last source argument is used as a constant. (That is, the + * last argument must have a type which indicates it is a known constant.) + * The instruction associated must have exactly two sources. + * + * @param opcode {@code non-null;} the opcode + * @param sourceA {@code non-null;} the first source + * @param sourceB {@code non-null;} the second source + * @return {@code true} iff the target can represent the operation + * using a constant for the last argument + */ + public boolean hasConstantOperation(Rop opcode, RegisterSpec sourceA, RegisterSpec sourceB); - /** - * Returns true if the translation target requires the sources of the - * specified opcode to be in order and contiguous (eg, for an invoke-range) - * - * @param opcode {@code non-null;} opcode - * @param sources {@code non-null;} source list - * @return {@code true} iff the target requires the sources to be - * in order and contiguous. - */ - public boolean requiresSourcesInOrder(Rop opcode, RegisterSpecList sources); + /** + * Returns true if the translation target requires the sources of the + * specified opcode to be in order and contiguous (eg, for an invoke-range) + * + * @param opcode {@code non-null;} opcode + * @param sources {@code non-null;} source list + * @return {@code true} iff the target requires the sources to be + * in order and contiguous. + */ + public boolean requiresSourcesInOrder(Rop opcode, RegisterSpecList sources); - /** - * Gets the maximum register width that can be represented optimally. - * For example, Dex bytecode does not have instruction forms that take - * register numbers larger than 15 for all instructions so - * DexTranslationAdvice returns 15 here. - * - * @return register count noted above - */ - public int getMaxOptimalRegisterCount(); + /** + * Gets the maximum register width that can be represented optimally. + * For example, Dex bytecode does not have instruction forms that take + * register numbers larger than 15 for all instructions so + * DexTranslationAdvice returns 15 here. + * + * @return register count noted above + */ + public int getMaxOptimalRegisterCount(); } diff --git a/dx/src/com/android/jack/dx/rop/cst/Constant.java b/dx/src/com/android/jack/dx/rop/cst/Constant.java index 7c756e2..a0d572d 100644 --- a/dx/src/com/android/jack/dx/rop/cst/Constant.java +++ b/dx/src/com/android/jack/dx/rop/cst/Constant.java @@ -21,48 +21,48 @@ import com.android.jack.dx.util.ToHuman; /** * Base class for constants of all sorts. */ -public abstract class Constant - implements ToHuman, Comparable<Constant> { - /** - * Returns {@code true} if this instance is a category-2 constant, - * meaning it takes up two slots in the constant pool, or - * {@code false} if this instance is category-1. - * - * @return {@code true} iff this instance is category-2 - */ - public abstract boolean isCategory2(); +public abstract class Constant implements ToHuman, Comparable<Constant> { + /** + * Returns {@code true} if this instance is a category-2 constant, + * meaning it takes up two slots in the constant pool, or + * {@code false} if this instance is category-1. + * + * @return {@code true} iff this instance is category-2 + */ + public abstract boolean isCategory2(); - /** - * Returns the human name for the particular type of constant - * this instance is. - * - * @return {@code non-null;} the name - */ - public abstract String typeName(); + /** + * Returns the human name for the particular type of constant + * this instance is. + * + * @return {@code non-null;} the name + */ + public abstract String typeName(); - /** - * {@inheritDoc} - * - * This compares in class-major and value-minor order. - */ - public final int compareTo(Constant other) { - Class clazz = getClass(); - Class otherClazz = other.getClass(); + /** + * {@inheritDoc} + * + * This compares in class-major and value-minor order. + */ + @Override + public final int compareTo(Constant other) { + Class<? extends Constant> clazz = getClass(); + Class<? extends Constant> otherClazz = other.getClass(); - if (clazz != otherClazz) { - return clazz.getName().compareTo(otherClazz.getName()); - } - - return compareTo0(other); + if (clazz != otherClazz) { + return clazz.getName().compareTo(otherClazz.getName()); } - /** - * Compare the values of this and another instance, which are guaranteed - * to be of the same class. Subclasses must implement this. - * - * @param other {@code non-null;} the instance to compare to - * @return {@code -1}, {@code 0}, or {@code 1}, as usual - * for a comparison - */ - protected abstract int compareTo0(Constant other); + return compareTo0(other); + } + + /** + * Compare the values of this and another instance, which are guaranteed + * to be of the same class. Subclasses must implement this. + * + * @param other {@code non-null;} the instance to compare to + * @return {@code -1}, {@code 0}, or {@code 1}, as usual + * for a comparison + */ + protected abstract int compareTo0(Constant other); } diff --git a/dx/src/com/android/jack/dx/rop/cst/ConstantPool.java b/dx/src/com/android/jack/dx/rop/cst/ConstantPool.java index 218f3c4..c1099fe 100644 --- a/dx/src/com/android/jack/dx/rop/cst/ConstantPool.java +++ b/dx/src/com/android/jack/dx/rop/cst/ConstantPool.java @@ -21,50 +21,50 @@ package com.android.jack.dx.rop.cst; * {@link Constant} objects. */ public interface ConstantPool { - /** - * Get the "size" of the constant pool. This corresponds to the - * class file field {@code constant_pool_count}, and is in fact - * always at least one more than the actual size of the constant pool, - * as element {@code 0} is always invalid. - * - * @return {@code >= 1;} the size - */ - public int size(); + /** + * Get the "size" of the constant pool. This corresponds to the + * class file field {@code constant_pool_count}, and is in fact + * always at least one more than the actual size of the constant pool, + * as element {@code 0} is always invalid. + * + * @return {@code >= 1;} the size + */ + public int size(); - /** - * Get the {@code n}th entry in the constant pool, which must - * be valid. - * - * @param n {@code n >= 0, n < size();} the constant pool index - * @return {@code non-null;} the corresponding entry - * @throws IllegalArgumentException thrown if {@code n} is - * in-range but invalid - */ - public Constant get(int n); + /** + * Get the {@code n}th entry in the constant pool, which must + * be valid. + * + * @param n {@code n >= 0, n < size();} the constant pool index + * @return {@code non-null;} the corresponding entry + * @throws IllegalArgumentException thrown if {@code n} is + * in-range but invalid + */ + public Constant get(int n); - /** - * Get the {@code n}th entry in the constant pool, which must - * be valid unless {@code n == 0}, in which case {@code null} - * is returned. - * - * @param n {@code n >= 0, n < size();} the constant pool index - * @return {@code null-ok;} the corresponding entry, if {@code n != 0} - * @throws IllegalArgumentException thrown if {@code n} is - * in-range and non-zero but invalid - */ - public Constant get0Ok(int n); + /** + * Get the {@code n}th entry in the constant pool, which must + * be valid unless {@code n == 0}, in which case {@code null} + * is returned. + * + * @param n {@code n >= 0, n < size();} the constant pool index + * @return {@code null-ok;} the corresponding entry, if {@code n != 0} + * @throws IllegalArgumentException thrown if {@code n} is + * in-range and non-zero but invalid + */ + public Constant get0Ok(int n); - /** - * Get the {@code n}th entry in the constant pool, or - * {@code null} if the index is in-range but invalid. In - * particular, {@code null} is returned for index {@code 0} - * as well as the index after any entry which is defined to take up - * two slots (that is, {@code Long} and {@code Double} - * entries). - * - * @param n {@code n >= 0, n < size();} the constant pool index - * @return {@code null-ok;} the corresponding entry, or {@code null} if - * the index is in-range but invalid - */ - public Constant getOrNull(int n); + /** + * Get the {@code n}th entry in the constant pool, or + * {@code null} if the index is in-range but invalid. In + * particular, {@code null} is returned for index {@code 0} + * as well as the index after any entry which is defined to take up + * two slots (that is, {@code Long} and {@code Double} + * entries). + * + * @param n {@code n >= 0, n < size();} the constant pool index + * @return {@code null-ok;} the corresponding entry, or {@code null} if + * the index is in-range but invalid + */ + public Constant getOrNull(int n); } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstAnnotation.java b/dx/src/com/android/jack/dx/rop/cst/CstAnnotation.java index 84dd61f..87a97ca 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstAnnotation.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstAnnotation.java @@ -22,75 +22,76 @@ import com.android.jack.dx.rop.annotation.Annotation; * Constant type that represents an annotation. */ public final class CstAnnotation extends Constant { - /** {@code non-null;} the actual annotation */ - private final Annotation annotation; + /** {@code non-null;} the actual annotation */ + private final Annotation annotation; - /** - * Constructs an instance. - * - * @param annotation {@code non-null;} the annotation to hold - */ - public CstAnnotation(Annotation annotation) { - if (annotation == null) { - throw new NullPointerException("annotation == null"); - } - - annotation.throwIfMutable(); - - this.annotation = annotation; + /** + * Constructs an instance. + * + * @param annotation {@code non-null;} the annotation to hold + */ + public CstAnnotation(Annotation annotation) { + if (annotation == null) { + throw new NullPointerException("annotation == null"); } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (! (other instanceof CstAnnotation)) { - return false; - } + annotation.throwIfMutable(); - return annotation.equals(((CstAnnotation) other).annotation); - } + this.annotation = annotation; + } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return annotation.hashCode(); + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof CstAnnotation)) { + return false; } - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - return annotation.compareTo(((CstAnnotation) other).annotation); - } + return annotation.equals(((CstAnnotation) other).annotation); + } - /** {@inheritDoc} */ - @Override - public String toString() { - return annotation.toString(); - } + /** {@inheritDoc} */ + @Override + public int hashCode() { + return annotation.hashCode(); + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "annotation"; - } + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + return annotation.compareTo(((CstAnnotation) other).annotation); + } - /** {@inheritDoc} */ - @Override - public boolean isCategory2() { - return false; - } + /** {@inheritDoc} */ + @Override + public String toString() { + return annotation.toString(); + } - /** {@inheritDoc} */ - public String toHuman() { - return annotation.toString(); - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "annotation"; + } - /** - * Get the underlying annotation. - * - * @return {@code non-null;} the annotation - */ - public Annotation getAnnotation() { - return annotation; - } + /** {@inheritDoc} */ + @Override + public boolean isCategory2() { + return false; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return annotation.toString(); + } + + /** + * Get the underlying annotation. + * + * @return {@code non-null;} the annotation + */ + public Annotation getAnnotation() { + return annotation; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstArray.java b/dx/src/com/android/jack/dx/rop/cst/CstArray.java index 080bb69..4d1c745 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstArray.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstArray.java @@ -16,144 +16,144 @@ package com.android.jack.dx.rop.cst; -import com.android.jack.dx.rop.type.Type; import com.android.jack.dx.util.FixedSizeList; /** * Constant type to represent a fixed array of other constants. */ public final class CstArray extends Constant { - /** {@code non-null;} the actual list of contents */ - private final List list; - - /** - * Constructs an instance. - * - * @param list {@code non-null;} the actual list of contents - */ - public CstArray(List list) { - if (list == null) { - throw new NullPointerException("list == null"); - } - - list.throwIfMutable(); - - this.list = list; + /** {@code non-null;} the actual list of contents */ + private final List list; + + /** + * Constructs an instance. + * + * @param list {@code non-null;} the actual list of contents + */ + public CstArray(List list) { + if (list == null) { + throw new NullPointerException("list == null"); } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (! (other instanceof CstArray)) { - return false; - } - - return list.equals(((CstArray) other).list); - } + list.throwIfMutable(); - /** {@inheritDoc} */ - @Override - public int hashCode() { - return list.hashCode(); - } + this.list = list; + } - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - return list.compareTo(((CstArray) other).list); + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof CstArray)) { + return false; } - /** {@inheritDoc} */ - @Override - public String toString() { - return list.toString("array{", ", ", "}"); + return list.equals(((CstArray) other).list); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return list.hashCode(); + } + + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + return list.compareTo(((CstArray) other).list); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return list.toString("array{", ", ", "}"); + } + + /** {@inheritDoc} */ + @Override + public String typeName() { + return "array"; + } + + /** {@inheritDoc} */ + @Override + public boolean isCategory2() { + return false; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return list.toHuman("{", ", ", "}"); + } + + /** + * Get the underlying list. + * + * @return {@code non-null;} the list + */ + public List getList() { + return list; + } + + /** + * List of {@link Constant} instances. + */ + public static final class List extends FixedSizeList implements Comparable<List> { + /** + * Constructs an instance. All indices initially contain + * {@code null}. + * + * @param size the size of the list + */ + public List(int size) { + super(size); } /** {@inheritDoc} */ @Override - public String typeName() { - return "array"; - } + public int compareTo(List other) { + int thisSize = size(); + int otherSize = other.size(); + int compareSize = (thisSize < otherSize) ? thisSize : otherSize; + + for (int i = 0; i < compareSize; i++) { + Constant thisItem = (Constant) get0(i); + Constant otherItem = (Constant) other.get0(i); + int compare = thisItem.compareTo(otherItem); + if (compare != 0) { + return compare; + } + } - /** {@inheritDoc} */ - @Override - public boolean isCategory2() { - return false; - } + if (thisSize < otherSize) { + return -1; + } else if (thisSize > otherSize) { + return 1; + } - /** {@inheritDoc} */ - public String toHuman() { - return list.toHuman("{", ", ", "}"); + return 0; } /** - * Get the underlying list. + * Gets the element at the given index. It is an error to call + * this with the index for an element which was never set; if you + * do that, this will throw {@code NullPointerException}. * - * @return {@code non-null;} the list + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ - public List getList() { - return list; + public Constant get(int n) { + return (Constant) get0(n); } /** - * List of {@link Constant} instances. + * Sets the element at the given index. + * + * @param n {@code >= 0, < size();} which index + * @param a {@code null-ok;} the element to set at {@code n} */ - public static final class List - extends FixedSizeList implements Comparable<List> { - /** - * Constructs an instance. All indices initially contain - * {@code null}. - * - * @param size the size of the list - */ - public List(int size) { - super(size); - } - - /** {@inheritDoc} */ - public int compareTo(List other) { - int thisSize = size(); - int otherSize = other.size(); - int compareSize = (thisSize < otherSize) ? thisSize : otherSize; - - for (int i = 0; i < compareSize; i++) { - Constant thisItem = (Constant) get0(i); - Constant otherItem = (Constant) other.get0(i); - int compare = thisItem.compareTo(otherItem); - if (compare != 0) { - return compare; - } - } - - if (thisSize < otherSize) { - return -1; - } else if (thisSize > otherSize) { - return 1; - } - - return 0; - } - - /** - * Gets the element at the given index. It is an error to call - * this with the index for an element which was never set; if you - * do that, this will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which index - * @return {@code non-null;} element at that index - */ - public Constant get(int n) { - return (Constant) get0(n); - } - - /** - * Sets the element at the given index. - * - * @param n {@code >= 0, < size();} which index - * @param a {@code null-ok;} the element to set at {@code n} - */ - public void set(int n, Constant a) { - set0(n, a); - } + public void set(int n, Constant a) { + set0(n, a); } + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstBaseMethodRef.java b/dx/src/com/android/jack/dx/rop/cst/CstBaseMethodRef.java index fedb208..1ba7d88 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstBaseMethodRef.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstBaseMethodRef.java @@ -18,134 +18,133 @@ package com.android.jack.dx.rop.cst; import com.android.jack.dx.rop.type.Prototype; import com.android.jack.dx.rop.type.Type; -import com.android.jack.dx.rop.type.TypeBearer; /** * Base class for constants of "methodish" type. * - * <p><b>Note:</b> As a {@link TypeBearer}, this class bears the return type + * <p><b>Note:</b> As a {@code TypeBearer}, this class bears the return type * of the method.</p> */ -public abstract class CstBaseMethodRef - extends CstMemberRef { - /** {@code non-null;} the raw prototype for this method */ - private final Prototype prototype; +public abstract class CstBaseMethodRef extends CstMemberRef { + /** {@code non-null;} the raw prototype for this method */ + private final Prototype prototype; - /** - * {@code null-ok;} the prototype for this method taken to be an instance - * method, or {@code null} if not yet calculated - */ - private Prototype instancePrototype; + /** + * {@code null-ok;} the prototype for this method taken to be an instance + * method, or {@code null} if not yet calculated + */ + private Prototype instancePrototype; - /** - * Constructs an instance. - * - * @param definingClass {@code non-null;} the type of the defining class - * @param nat {@code non-null;} the name-and-type - */ - /*package*/ CstBaseMethodRef(CstType definingClass, CstNat nat) { - super(definingClass, nat); + /** + * Constructs an instance. + * + * @param definingClass {@code non-null;} the type of the defining class + * @param nat {@code non-null;} the name-and-type + */ + /*package*/CstBaseMethodRef(CstType definingClass, CstNat nat) { + super(definingClass, nat); - String descriptor = getNat().getDescriptor().getString(); - this.prototype = Prototype.intern(descriptor); - this.instancePrototype = null; - } + String descriptor = getNat().getDescriptor().getString(); + this.prototype = Prototype.intern(descriptor); + this.instancePrototype = null; + } - /** - * Gets the raw prototype of this method. This doesn't include a - * {@code this} argument. - * - * @return {@code non-null;} the method prototype - */ - public final Prototype getPrototype() { - return prototype; - } + /** + * Gets the raw prototype of this method. This doesn't include a + * {@code this} argument. + * + * @return {@code non-null;} the method prototype + */ + public final Prototype getPrototype() { + return prototype; + } - /** - * Gets the prototype of this method as either a - * {@code static} or instance method. In the case of a - * {@code static} method, this is the same as the raw - * prototype. In the case of an instance method, this has an - * appropriately-typed {@code this} argument as the first - * one. - * - * @param isStatic whether the method should be considered static - * @return {@code non-null;} the method prototype - */ - public final Prototype getPrototype(boolean isStatic) { - if (isStatic) { - return prototype; - } else { - if (instancePrototype == null) { - Type thisType = getDefiningClass().getClassType(); - instancePrototype = prototype.withFirstParameter(thisType); - } - return instancePrototype; - } + /** + * Gets the prototype of this method as either a + * {@code static} or instance method. In the case of a + * {@code static} method, this is the same as the raw + * prototype. In the case of an instance method, this has an + * appropriately-typed {@code this} argument as the first + * one. + * + * @param isStatic whether the method should be considered static + * @return {@code non-null;} the method prototype + */ + public final Prototype getPrototype(boolean isStatic) { + if (isStatic) { + return prototype; + } else { + if (instancePrototype == null) { + Type thisType = getDefiningClass().getClassType(); + instancePrototype = prototype.withFirstParameter(thisType); + } + return instancePrototype; } + } - /** {@inheritDoc} */ - @Override - protected final int compareTo0(Constant other) { - int cmp = super.compareTo0(other); + /** {@inheritDoc} */ + @Override + protected final int compareTo0(Constant other) { + int cmp = super.compareTo0(other); - if (cmp != 0) { - return cmp; - } - - CstBaseMethodRef otherMethod = (CstBaseMethodRef) other; - return prototype.compareTo(otherMethod.prototype); + if (cmp != 0) { + return cmp; } - /** - * {@inheritDoc} - * - * In this case, this method returns the <i>return type</i> of this method. - * - * @return {@code non-null;} the method's return type - */ - public final Type getType() { - return prototype.getReturnType(); - } + CstBaseMethodRef otherMethod = (CstBaseMethodRef) other; + return prototype.compareTo(otherMethod.prototype); + } - /** - * Gets the number of words of parameters required by this - * method's descriptor. Since instances of this class have no way - * to know if they will be used in a {@code static} or - * instance context, one has to indicate this explicitly as an - * argument. This method is just a convenient shorthand for - * {@code getPrototype().getParameterTypes().getWordCount()}, - * plus {@code 1} if the method is to be treated as an - * instance method. - * - * @param isStatic whether the method should be considered static - * @return {@code >= 0;} the argument word count - */ - public final int getParameterWordCount(boolean isStatic) { - return getPrototype(isStatic).getParameterTypes().getWordCount(); - } + /** + * {@inheritDoc} + * + * In this case, this method returns the <i>return type</i> of this method. + * + * @return {@code non-null;} the method's return type + */ + @Override + public final Type getType() { + return prototype.getReturnType(); + } - /** - * Gets whether this is a reference to an instance initialization - * method. This is just a convenient shorthand for - * {@code getNat().isInstanceInit()}. - * - * @return {@code true} iff this is a reference to an - * instance initialization method - */ - public final boolean isInstanceInit() { - return getNat().isInstanceInit(); - } + /** + * Gets the number of words of parameters required by this + * method's descriptor. Since instances of this class have no way + * to know if they will be used in a {@code static} or + * instance context, one has to indicate this explicitly as an + * argument. This method is just a convenient shorthand for + * {@code getPrototype().getParameterTypes().getWordCount()}, + * plus {@code 1} if the method is to be treated as an + * instance method. + * + * @param isStatic whether the method should be considered static + * @return {@code >= 0;} the argument word count + */ + public final int getParameterWordCount(boolean isStatic) { + return getPrototype(isStatic).getParameterTypes().getWordCount(); + } - /** - * Gets whether this is a reference to a class initialization - * method. This is just a convenient shorthand for - * {@code getNat().isClassInit()}. - * - * @return {@code true} iff this is a reference to an - * instance initialization method - */ - public final boolean isClassInit() { - return getNat().isClassInit(); - } + /** + * Gets whether this is a reference to an instance initialization + * method. This is just a convenient shorthand for + * {@code getNat().isInstanceInit()}. + * + * @return {@code true} iff this is a reference to an + * instance initialization method + */ + public final boolean isInstanceInit() { + return getNat().isInstanceInit(); + } + + /** + * Gets whether this is a reference to a class initialization + * method. This is just a convenient shorthand for + * {@code getNat().isClassInit()}. + * + * @return {@code true} iff this is a reference to an + * instance initialization method + */ + public final boolean isClassInit() { + return getNat().isClassInit(); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstBoolean.java b/dx/src/com/android/jack/dx/rop/cst/CstBoolean.java index 81a0e27..ad4a063 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstBoolean.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstBoolean.java @@ -21,79 +21,80 @@ import com.android.jack.dx.rop.type.Type; /** * Constants of type {@code boolean}. */ -public final class CstBoolean - extends CstLiteral32 { - /** {@code non-null;} instance representing {@code false} */ - public static final CstBoolean VALUE_FALSE = new CstBoolean(false); +public final class CstBoolean extends CstLiteral32 { + /** {@code non-null;} instance representing {@code false} */ + public static final CstBoolean VALUE_FALSE = new CstBoolean(false); - /** {@code non-null;} instance representing {@code true} */ - public static final CstBoolean VALUE_TRUE = new CstBoolean(true); + /** {@code non-null;} instance representing {@code true} */ + public static final CstBoolean VALUE_TRUE = new CstBoolean(true); - /** - * Makes an instance for the given value. This will return an - * already-allocated instance. - * - * @param value the {@code boolean} value - * @return {@code non-null;} the appropriate instance - */ - public static CstBoolean make(boolean value) { - return value ? VALUE_TRUE : VALUE_FALSE; - } + /** + * Makes an instance for the given value. This will return an + * already-allocated instance. + * + * @param value the {@code boolean} value + * @return {@code non-null;} the appropriate instance + */ + public static CstBoolean make(boolean value) { + return value ? VALUE_TRUE : VALUE_FALSE; + } - /** - * Makes an instance for the given {@code int} value. This - * will return an already-allocated instance. - * - * @param value must be either {@code 0} or {@code 1} - * @return {@code non-null;} the appropriate instance - */ - public static CstBoolean make(int value) { - if (value == 0) { - return VALUE_FALSE; - } else if (value == 1) { - return VALUE_TRUE; - } else { - throw new IllegalArgumentException("bogus value: " + value); - } + /** + * Makes an instance for the given {@code int} value. This + * will return an already-allocated instance. + * + * @param value must be either {@code 0} or {@code 1} + * @return {@code non-null;} the appropriate instance + */ + public static CstBoolean make(int value) { + if (value == 0) { + return VALUE_FALSE; + } else if (value == 1) { + return VALUE_TRUE; + } else { + throw new IllegalArgumentException("bogus value: " + value); } + } - /** - * Constructs an instance. This constructor is private; use {@link #make}. - * - * @param value the {@code boolean} value - */ - private CstBoolean(boolean value) { - super(value ? 1 : 0); - } + /** + * Constructs an instance. This constructor is private; use {@link #make}. + * + * @param value the {@code boolean} value + */ + private CstBoolean(boolean value) { + super(value ? 1 : 0); + } - /** {@inheritDoc} */ - @Override - public String toString() { - return getValue() ? "boolean{true}" : "boolean{false}"; - } + /** {@inheritDoc} */ + @Override + public String toString() { + return getValue() ? "boolean{true}" : "boolean{false}"; + } - /** {@inheritDoc} */ - public Type getType() { - return Type.BOOLEAN; - } + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.BOOLEAN; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "boolean"; - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "boolean"; + } - /** {@inheritDoc} */ - public String toHuman() { - return getValue() ? "true" : "false"; - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return getValue() ? "true" : "false"; + } - /** - * Gets the {@code boolean} value. - * - * @return the value - */ - public boolean getValue() { - return (getIntBits() == 0) ? false : true; - } + /** + * Gets the {@code boolean} value. + * + * @return the value + */ + public boolean getValue() { + return (getIntBits() == 0) ? false : true; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstByte.java b/dx/src/com/android/jack/dx/rop/cst/CstByte.java index 5ab4188..f92bfd0 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstByte.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstByte.java @@ -22,78 +22,78 @@ import com.android.jack.dx.util.Hex; /** * Constants of type {@code byte}. */ -public final class CstByte - extends CstLiteral32 { - /** {@code non-null;} the value {@code 0} as an instance of this class */ - public static final CstByte VALUE_0 = make((byte) 0); +public final class CstByte extends CstLiteral32 { + /** {@code non-null;} the value {@code 0} as an instance of this class */ + public static final CstByte VALUE_0 = make((byte) 0); - /** - * Makes an instance for the given value. This may (but does not - * necessarily) return an already-allocated instance. - * - * @param value the {@code byte} value - */ - public static CstByte make(byte value) { - return new CstByte(value); - } - - /** - * Makes an instance for the given {@code int} value. This - * may (but does not necessarily) return an already-allocated - * instance. - * - * @param value the value, which must be in range for a {@code byte} - * @return {@code non-null;} the appropriate instance - */ - public static CstByte make(int value) { - byte cast = (byte) value; + /** + * Makes an instance for the given value. This may (but does not + * necessarily) return an already-allocated instance. + * + * @param value the {@code byte} value + */ + public static CstByte make(byte value) { + return new CstByte(value); + } - if (cast != value) { - throw new IllegalArgumentException("bogus byte value: " + - value); - } + /** + * Makes an instance for the given {@code int} value. This + * may (but does not necessarily) return an already-allocated + * instance. + * + * @param value the value, which must be in range for a {@code byte} + * @return {@code non-null;} the appropriate instance + */ + public static CstByte make(int value) { + byte cast = (byte) value; - return make(cast); + if (cast != value) { + throw new IllegalArgumentException("bogus byte value: " + value); } - /** - * Constructs an instance. This constructor is private; use {@link #make}. - * - * @param value the {@code byte} value - */ - private CstByte(byte value) { - super(value); - } + return make(cast); + } - /** {@inheritDoc} */ - @Override - public String toString() { - int value = getIntBits(); - return "byte{0x" + Hex.u1(value) + " / " + value + '}'; - } + /** + * Constructs an instance. This constructor is private; use {@link #make}. + * + * @param value the {@code byte} value + */ + private CstByte(byte value) { + super(value); + } - /** {@inheritDoc} */ - public Type getType() { - return Type.BYTE; - } + /** {@inheritDoc} */ + @Override + public String toString() { + int value = getIntBits(); + return "byte{0x" + Hex.u1(value) + " / " + value + '}'; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "byte"; - } + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.BYTE; + } - /** {@inheritDoc} */ - public String toHuman() { - return Integer.toString(getIntBits()); - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "byte"; + } - /** - * Gets the {@code byte} value. - * - * @return the value - */ - public byte getValue() { - return (byte) getIntBits(); - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return Integer.toString(getIntBits()); + } + + /** + * Gets the {@code byte} value. + * + * @return the value + */ + public byte getValue() { + return (byte) getIntBits(); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstChar.java b/dx/src/com/android/jack/dx/rop/cst/CstChar.java index b4cd303..3804542 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstChar.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstChar.java @@ -22,78 +22,78 @@ import com.android.jack.dx.util.Hex; /** * Constants of type {@code char}. */ -public final class CstChar - extends CstLiteral32 { - /** {@code non-null;} the value {@code 0} as an instance of this class */ - public static final CstChar VALUE_0 = make((char) 0); +public final class CstChar extends CstLiteral32 { + /** {@code non-null;} the value {@code 0} as an instance of this class */ + public static final CstChar VALUE_0 = make((char) 0); - /** - * Makes an instance for the given value. This may (but does not - * necessarily) return an already-allocated instance. - * - * @param value the {@code char} value - */ - public static CstChar make(char value) { - return new CstChar(value); - } - - /** - * Makes an instance for the given {@code int} value. This - * may (but does not necessarily) return an already-allocated - * instance. - * - * @param value the value, which must be in range for a {@code char} - * @return {@code non-null;} the appropriate instance - */ - public static CstChar make(int value) { - char cast = (char) value; + /** + * Makes an instance for the given value. This may (but does not + * necessarily) return an already-allocated instance. + * + * @param value the {@code char} value + */ + public static CstChar make(char value) { + return new CstChar(value); + } - if (cast != value) { - throw new IllegalArgumentException("bogus char value: " + - value); - } + /** + * Makes an instance for the given {@code int} value. This + * may (but does not necessarily) return an already-allocated + * instance. + * + * @param value the value, which must be in range for a {@code char} + * @return {@code non-null;} the appropriate instance + */ + public static CstChar make(int value) { + char cast = (char) value; - return make(cast); + if (cast != value) { + throw new IllegalArgumentException("bogus char value: " + value); } - /** - * Constructs an instance. This constructor is private; use {@link #make}. - * - * @param value the {@code char} value - */ - private CstChar(char value) { - super(value); - } + return make(cast); + } - /** {@inheritDoc} */ - @Override - public String toString() { - int value = getIntBits(); - return "char{0x" + Hex.u2(value) + " / " + value + '}'; - } + /** + * Constructs an instance. This constructor is private; use {@link #make}. + * + * @param value the {@code char} value + */ + private CstChar(char value) { + super(value); + } - /** {@inheritDoc} */ - public Type getType() { - return Type.CHAR; - } + /** {@inheritDoc} */ + @Override + public String toString() { + int value = getIntBits(); + return "char{0x" + Hex.u2(value) + " / " + value + '}'; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "char"; - } + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.CHAR; + } - /** {@inheritDoc} */ - public String toHuman() { - return Integer.toString(getIntBits()); - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "char"; + } - /** - * Gets the {@code char} value. - * - * @return the value - */ - public char getValue() { - return (char) getIntBits(); - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return Integer.toString(getIntBits()); + } + + /** + * Gets the {@code char} value. + * + * @return the value + */ + public char getValue() { + return (char) getIntBits(); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstDouble.java b/dx/src/com/android/jack/dx/rop/cst/CstDouble.java index c000e6a..46d4c72 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstDouble.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstDouble.java @@ -22,69 +22,67 @@ import com.android.jack.dx.util.Hex; /** * Constants of type {@code CONSTANT_Double_info}. */ -public final class CstDouble - extends CstLiteral64 { - /** {@code non-null;} instance representing {@code 0} */ - public static final CstDouble VALUE_0 = - new CstDouble(Double.doubleToLongBits(0.0)); +public final class CstDouble extends CstLiteral64 { + /** {@code non-null;} instance representing {@code 0} */ + public static final CstDouble VALUE_0 = new CstDouble(Double.doubleToLongBits(0.0)); - /** {@code non-null;} instance representing {@code 1} */ - public static final CstDouble VALUE_1 = - new CstDouble(Double.doubleToLongBits(1.0)); + /** {@code non-null;} instance representing {@code 1} */ + public static final CstDouble VALUE_1 = new CstDouble(Double.doubleToLongBits(1.0)); - /** - * Makes an instance for the given value. This may (but does not - * necessarily) return an already-allocated instance. - * - * @param bits the {@code double} value as {@code long} bits + /** + * Makes an instance for the given value. This may (but does not + * necessarily) return an already-allocated instance. + * + * @param bits the {@code double} value as {@code long} bits + */ + public static CstDouble make(long bits) { + /* + * Note: Javadoc notwithstanding, this implementation always + * allocates. */ - public static CstDouble make(long bits) { - /* - * Note: Javadoc notwithstanding, this implementation always - * allocates. - */ - return new CstDouble(bits); - } + return new CstDouble(bits); + } - /** - * Constructs an instance. This constructor is private; use {@link #make}. - * - * @param bits the {@code double} value as {@code long} bits - */ - private CstDouble(long bits) { - super(bits); - } + /** + * Constructs an instance. This constructor is private; use {@link #make}. + * + * @param bits the {@code double} value as {@code long} bits + */ + private CstDouble(long bits) { + super(bits); + } - /** {@inheritDoc} */ - @Override - public String toString() { - long bits = getLongBits(); - return "double{0x" + Hex.u8(bits) + " / " + - Double.longBitsToDouble(bits) + '}'; - } + /** {@inheritDoc} */ + @Override + public String toString() { + long bits = getLongBits(); + return "double{0x" + Hex.u8(bits) + " / " + Double.longBitsToDouble(bits) + '}'; + } - /** {@inheritDoc} */ - public Type getType() { - return Type.DOUBLE; - } + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.DOUBLE; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "double"; - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "double"; + } - /** {@inheritDoc} */ - public String toHuman() { - return Double.toString(Double.longBitsToDouble(getLongBits())); - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return Double.toString(Double.longBitsToDouble(getLongBits())); + } - /** - * Gets the {@code double} value. - * - * @return the value - */ - public double getValue() { - return Double.longBitsToDouble(getLongBits()); - } + /** + * Gets the {@code double} value. + * + * @return the value + */ + public double getValue() { + return Double.longBitsToDouble(getLongBits()); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstEnumRef.java b/dx/src/com/android/jack/dx/rop/cst/CstEnumRef.java index 9be4634..b2528e2 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstEnumRef.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstEnumRef.java @@ -23,46 +23,47 @@ import com.android.jack.dx.rop.type.Type; * value of an enumerated type. */ public final class CstEnumRef extends CstMemberRef { - /** {@code null-ok;} the corresponding field ref, lazily initialized */ - private CstFieldRef fieldRef; + /** {@code null-ok;} the corresponding field ref, lazily initialized */ + private CstFieldRef fieldRef; - /** - * Constructs an instance. - * - * @param nat {@code non-null;} the name-and-type; the defining class is derived - * from this - */ - public CstEnumRef(CstNat nat) { - super(new CstType(nat.getFieldType()), nat); + /** + * Constructs an instance. + * + * @param nat {@code non-null;} the name-and-type; the defining class is derived + * from this + */ + public CstEnumRef(CstNat nat) { + super(new CstType(nat.getFieldType()), nat); - fieldRef = null; - } + fieldRef = null; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "enum"; - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "enum"; + } - /** - * {@inheritDoc} - * - * <b>Note:</b> This returns the enumerated type. - */ - public Type getType() { - return getDefiningClass().getClassType(); - } + /** + * {@inheritDoc} + * + * <b>Note:</b> This returns the enumerated type. + */ + @Override + public Type getType() { + return getDefiningClass().getClassType(); + } - /** - * Get a {@link CstFieldRef} that corresponds with this instance. - * - * @return {@code non-null;} the corresponding field reference - */ - public CstFieldRef getFieldRef() { - if (fieldRef == null) { - fieldRef = new CstFieldRef(getDefiningClass(), getNat()); - } - - return fieldRef; + /** + * Get a {@link CstFieldRef} that corresponds with this instance. + * + * @return {@code non-null;} the corresponding field reference + */ + public CstFieldRef getFieldRef() { + if (fieldRef == null) { + fieldRef = new CstFieldRef(getDefiningClass(), getNat()); } + + return fieldRef; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstFieldRef.java b/dx/src/com/android/jack/dx/rop/cst/CstFieldRef.java index 76e7f25..3121114 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstFieldRef.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstFieldRef.java @@ -22,58 +22,58 @@ import com.android.jack.dx.rop.type.Type; * Constants of type {@code CONSTANT_Fieldref_info}. */ public final class CstFieldRef extends CstMemberRef { - /** - * Returns an instance of this class that represents the static - * field which should hold the class corresponding to a given - * primitive type. For example, if given {@link Type#INT}, this - * method returns an instance corresponding to the field - * {@code java.lang.Integer.TYPE}. - * - * @param primitiveType {@code non-null;} the primitive type - * @return {@code non-null;} the corresponding static field - */ - public static CstFieldRef forPrimitiveType(Type primitiveType) { - return new CstFieldRef(CstType.forBoxedPrimitiveType(primitiveType), - CstNat.PRIMITIVE_TYPE_NAT); - } - - /** - * Constructs an instance. - * - * @param definingClass {@code non-null;} the type of the defining class - * @param nat {@code non-null;} the name-and-type - */ - public CstFieldRef(CstType definingClass, CstNat nat) { - super(definingClass, nat); - } + /** + * Returns an instance of this class that represents the static + * field which should hold the class corresponding to a given + * primitive type. For example, if given {@link Type#INT}, this + * method returns an instance corresponding to the field + * {@code java.lang.Integer.TYPE}. + * + * @param primitiveType {@code non-null;} the primitive type + * @return {@code non-null;} the corresponding static field + */ + public static CstFieldRef forPrimitiveType(Type primitiveType) { + return new CstFieldRef(CstType.forBoxedPrimitiveType(primitiveType), CstNat.PRIMITIVE_TYPE_NAT); + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "field"; - } + /** + * Constructs an instance. + * + * @param definingClass {@code non-null;} the type of the defining class + * @param nat {@code non-null;} the name-and-type + */ + public CstFieldRef(CstType definingClass, CstNat nat) { + super(definingClass, nat); + } - /** - * Returns the type of this field. - * - * @return {@code non-null;} the field's type - */ - public Type getType() { - return getNat().getFieldType(); - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "field"; + } - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - int cmp = super.compareTo0(other); + /** + * Returns the type of this field. + * + * @return {@code non-null;} the field's type + */ + @Override + public Type getType() { + return getNat().getFieldType(); + } - if (cmp != 0) { - return cmp; - } + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + int cmp = super.compareTo0(other); - CstFieldRef otherField = (CstFieldRef) other; - CstString thisDescriptor = getNat().getDescriptor(); - CstString otherDescriptor = otherField.getNat().getDescriptor(); - return thisDescriptor.compareTo(otherDescriptor); + if (cmp != 0) { + return cmp; } + + CstFieldRef otherField = (CstFieldRef) other; + CstString thisDescriptor = getNat().getDescriptor(); + CstString otherDescriptor = otherField.getNat().getDescriptor(); + return thisDescriptor.compareTo(otherDescriptor); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstFloat.java b/dx/src/com/android/jack/dx/rop/cst/CstFloat.java index e257fae..b599ae4 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstFloat.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstFloat.java @@ -22,70 +22,70 @@ import com.android.jack.dx.util.Hex; /** * Constants of type {@code CONSTANT_Float_info}. */ -public final class CstFloat - extends CstLiteral32 { - /** {@code non-null;} instance representing {@code 0} */ - public static final CstFloat VALUE_0 = make(Float.floatToIntBits(0.0f)); +public final class CstFloat extends CstLiteral32 { + /** {@code non-null;} instance representing {@code 0} */ + public static final CstFloat VALUE_0 = make(Float.floatToIntBits(0.0f)); - /** {@code non-null;} instance representing {@code 1} */ - public static final CstFloat VALUE_1 = make(Float.floatToIntBits(1.0f)); + /** {@code non-null;} instance representing {@code 1} */ + public static final CstFloat VALUE_1 = make(Float.floatToIntBits(1.0f)); - /** {@code non-null;} instance representing {@code 2} */ - public static final CstFloat VALUE_2 = make(Float.floatToIntBits(2.0f)); + /** {@code non-null;} instance representing {@code 2} */ + public static final CstFloat VALUE_2 = make(Float.floatToIntBits(2.0f)); - /** - * Makes an instance for the given value. This may (but does not - * necessarily) return an already-allocated instance. - * - * @param bits the {@code float} value as {@code int} bits + /** + * Makes an instance for the given value. This may (but does not + * necessarily) return an already-allocated instance. + * + * @param bits the {@code float} value as {@code int} bits + */ + public static CstFloat make(int bits) { + /* + * Note: Javadoc notwithstanding, this implementation always + * allocates. */ - public static CstFloat make(int bits) { - /* - * Note: Javadoc notwithstanding, this implementation always - * allocates. - */ - return new CstFloat(bits); - } + return new CstFloat(bits); + } - /** - * Constructs an instance. This constructor is private; use {@link #make}. - * - * @param bits the {@code float} value as {@code int} bits - */ - private CstFloat(int bits) { - super(bits); - } + /** + * Constructs an instance. This constructor is private; use {@link #make}. + * + * @param bits the {@code float} value as {@code int} bits + */ + private CstFloat(int bits) { + super(bits); + } - /** {@inheritDoc} */ - @Override - public String toString() { - int bits = getIntBits(); - return "float{0x" + Hex.u4(bits) + " / " + - Float.intBitsToFloat(bits) + '}'; - } + /** {@inheritDoc} */ + @Override + public String toString() { + int bits = getIntBits(); + return "float{0x" + Hex.u4(bits) + " / " + Float.intBitsToFloat(bits) + '}'; + } - /** {@inheritDoc} */ - public Type getType() { - return Type.FLOAT; - } + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.FLOAT; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "float"; - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "float"; + } - /** {@inheritDoc} */ - public String toHuman() { - return Float.toString(Float.intBitsToFloat(getIntBits())); - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return Float.toString(Float.intBitsToFloat(getIntBits())); + } - /** - * Gets the {@code float} value. - * - * @return the value - */ - public float getValue() { - return Float.intBitsToFloat(getIntBits()); - } + /** + * Gets the {@code float} value. + * + * @return the value + */ + public float getValue() { + return Float.intBitsToFloat(getIntBits()); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstIndexMap.java b/dx/src/com/android/jack/dx/rop/cst/CstIndexMap.java index 3d84830..9a5d55e 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstIndexMap.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstIndexMap.java @@ -15,13 +15,13 @@ */ package com.android.jack.dx.rop.cst; +import com.android.jack.dx.dex.file.DexFile; +import com.android.jack.dx.dex.file.IndexedItem; + import java.util.Collection; import java.util.HashMap; import java.util.Map; -import com.android.jack.dx.dex.file.DexFile; -import com.android.jack.dx.dex.file.IndexedItem; - /** * Maps {@link TypedConstant} index offsets from a dex file to those into another. */ @@ -38,8 +38,7 @@ public class CstIndexMap { new HashMap<Integer, CstBaseMethodRef>(); /** Mapping between index and {@link CstFieldRef} value of a dex file.*/ - private final Map<Integer, CstFieldRef> fieldsIndexMap = - new HashMap<Integer, CstFieldRef>(); + private final Map<Integer, CstFieldRef> fieldsIndexMap = new HashMap<Integer, CstFieldRef>(); /** * Keeps string mapping of a dex file. @@ -47,10 +46,9 @@ public class CstIndexMap { * @param cstString The string. */ public void addStringMapping(int index, CstString cstString) { - Integer key = new Integer (index); + Integer key = new Integer(index); assert index >= 0; - assert stringsIndexMap.get(key) == null - || stringsIndexMap.get(key).compareTo(cstString) == 0; + assert stringsIndexMap.get(key) == null || stringsIndexMap.get(key).compareTo(cstString) == 0; if (!stringsIndexMap.containsKey(key)) { stringsIndexMap.put(key, cstString); @@ -63,10 +61,9 @@ public class CstIndexMap { * @param cstType The type. */ public void addTypeMapping(int index, CstType cstType) { - Integer key = new Integer (index); + Integer key = new Integer(index); assert index >= 0; - assert typesIndexMap.get(key) == null - || typesIndexMap.get(key).compareTo(cstType) == 0; + assert typesIndexMap.get(key) == null || typesIndexMap.get(key).compareTo(cstType) == 0; if (!typesIndexMap.containsKey(key)) { typesIndexMap.put(key, cstType); @@ -79,10 +76,9 @@ public class CstIndexMap { * @param methodRef The method. */ public void addMethodMapping(int index, CstBaseMethodRef methodRef) { - Integer key = new Integer (index); + Integer key = new Integer(index); assert index >= 0; - assert methodsIndexMap.get(key) == null - || methodsIndexMap.get(key).compareTo(methodRef) == 0; + assert methodsIndexMap.get(key) == null || methodsIndexMap.get(key).compareTo(methodRef) == 0; if (!methodsIndexMap.containsKey(key)) { methodsIndexMap.put(key, methodRef); @@ -95,10 +91,9 @@ public class CstIndexMap { * @param fieldRef The Field. */ public void addFieldMapping(int index, CstFieldRef fieldRef) { - Integer key = new Integer (index); + Integer key = new Integer(index); assert index >= 0; - assert fieldsIndexMap.get(key) == null - || fieldsIndexMap.get(key).compareTo(fieldRef) == 0; + assert fieldsIndexMap.get(key) == null || fieldsIndexMap.get(key).compareTo(fieldRef) == 0; if (!fieldsIndexMap.containsKey(key)) { fieldsIndexMap.put(key, fieldRef); @@ -109,7 +104,7 @@ public class CstIndexMap { * Merge all {@link TypedConstant} of one dex file into another. * @param dex The dex file where values are merged. */ - public void mergeConstantsIntoDexFile(DexFile dex) { + public void mergeConstantsIntoDexFile(DexFile dex) { for (CstString cst : stringsIndexMap.values()) { dex.getStringIds().intern(cst); } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstInteger.java b/dx/src/com/android/jack/dx/rop/cst/CstInteger.java index 10ae236..e6ee29a 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstInteger.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstInteger.java @@ -22,95 +22,96 @@ import com.android.jack.dx.util.Hex; /** * Constants of type {@code CONSTANT_Integer_info}. */ -public final class CstInteger - extends CstLiteral32 { - /** {@code non-null;} array of cached instances */ - private static final CstInteger[] cache = new CstInteger[511]; - - /** {@code non-null;} instance representing {@code -1} */ - public static final CstInteger VALUE_M1 = make(-1); - - /** {@code non-null;} instance representing {@code 0} */ - public static final CstInteger VALUE_0 = make(0); - - /** {@code non-null;} instance representing {@code 1} */ - public static final CstInteger VALUE_1 = make(1); - - /** {@code non-null;} instance representing {@code 2} */ - public static final CstInteger VALUE_2 = make(2); - - /** {@code non-null;} instance representing {@code 3} */ - public static final CstInteger VALUE_3 = make(3); - - /** {@code non-null;} instance representing {@code 4} */ - public static final CstInteger VALUE_4 = make(4); - - /** {@code non-null;} instance representing {@code 5} */ - public static final CstInteger VALUE_5 = make(5); - - /** - * Makes an instance for the given value. This may (but does not - * necessarily) return an already-allocated instance. - * - * @param value the {@code int} value - * @return {@code non-null;} the appropriate instance - */ - public static CstInteger make(int value) { - /* - * Note: No need to synchronize, since we don't make any sort - * of guarantee about ==, and it's okay to overwrite existing - * entries too. - */ - int idx = (value & 0x7fffffff) % cache.length; - CstInteger obj = cache[idx]; - - if ((obj != null) && (obj.getValue() == value)) { - return obj; - } - - obj = new CstInteger(value); - cache[idx] = obj; - return obj; - } - - /** - * Constructs an instance. This constructor is private; use {@link #make}. - * - * @param value the {@code int} value +public final class CstInteger extends CstLiteral32 { + /** {@code non-null;} array of cached instances */ + private static final CstInteger[] cache = new CstInteger[511]; + + /** {@code non-null;} instance representing {@code -1} */ + public static final CstInteger VALUE_M1 = make(-1); + + /** {@code non-null;} instance representing {@code 0} */ + public static final CstInteger VALUE_0 = make(0); + + /** {@code non-null;} instance representing {@code 1} */ + public static final CstInteger VALUE_1 = make(1); + + /** {@code non-null;} instance representing {@code 2} */ + public static final CstInteger VALUE_2 = make(2); + + /** {@code non-null;} instance representing {@code 3} */ + public static final CstInteger VALUE_3 = make(3); + + /** {@code non-null;} instance representing {@code 4} */ + public static final CstInteger VALUE_4 = make(4); + + /** {@code non-null;} instance representing {@code 5} */ + public static final CstInteger VALUE_5 = make(5); + + /** + * Makes an instance for the given value. This may (but does not + * necessarily) return an already-allocated instance. + * + * @param value the {@code int} value + * @return {@code non-null;} the appropriate instance + */ + public static CstInteger make(int value) { + /* + * Note: No need to synchronize, since we don't make any sort + * of guarantee about ==, and it's okay to overwrite existing + * entries too. */ - private CstInteger(int value) { - super(value); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - int value = getIntBits(); - return "int{0x" + Hex.u4(value) + " / " + value + '}'; - } - - /** {@inheritDoc} */ - public Type getType() { - return Type.INT; - } - - /** {@inheritDoc} */ - @Override - public String typeName() { - return "int"; - } + int idx = (value & 0x7fffffff) % cache.length; + CstInteger obj = cache[idx]; - /** {@inheritDoc} */ - public String toHuman() { - return Integer.toString(getIntBits()); + if ((obj != null) && (obj.getValue() == value)) { + return obj; } - /** - * Gets the {@code int} value. - * - * @return the value - */ - public int getValue() { - return getIntBits(); - } + obj = new CstInteger(value); + cache[idx] = obj; + return obj; + } + + /** + * Constructs an instance. This constructor is private; use {@link #make}. + * + * @param value the {@code int} value + */ + private CstInteger(int value) { + super(value); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + int value = getIntBits(); + return "int{0x" + Hex.u4(value) + " / " + value + '}'; + } + + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.INT; + } + + /** {@inheritDoc} */ + @Override + public String typeName() { + return "int"; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return Integer.toString(getIntBits()); + } + + /** + * Gets the {@code int} value. + * + * @return the value + */ + public int getValue() { + return getIntBits(); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstInterfaceMethodRef.java b/dx/src/com/android/jack/dx/rop/cst/CstInterfaceMethodRef.java index 66c3dd6..7aef26a 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstInterfaceMethodRef.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstInterfaceMethodRef.java @@ -19,42 +19,41 @@ package com.android.jack.dx.rop.cst; /** * Constants of type {@code CONSTANT_InterfaceMethodref_info}. */ -public final class CstInterfaceMethodRef - extends CstBaseMethodRef { - /** - * {@code null-ok;} normal {@link CstMethodRef} that corresponds to this - * instance, if calculated - */ - private CstMethodRef methodRef; +public final class CstInterfaceMethodRef extends CstBaseMethodRef { + /** + * {@code null-ok;} normal {@link CstMethodRef} that corresponds to this + * instance, if calculated + */ + private CstMethodRef methodRef; - /** - * Constructs an instance. - * - * @param definingClass {@code non-null;} the type of the defining class - * @param nat {@code non-null;} the name-and-type - */ - public CstInterfaceMethodRef(CstType definingClass, CstNat nat) { - super(definingClass, nat); - methodRef = null; - } - - /** {@inheritDoc} */ - @Override - public String typeName() { - return "ifaceMethod"; - } + /** + * Constructs an instance. + * + * @param definingClass {@code non-null;} the type of the defining class + * @param nat {@code non-null;} the name-and-type + */ + public CstInterfaceMethodRef(CstType definingClass, CstNat nat) { + super(definingClass, nat); + methodRef = null; + } - /** - * Gets a normal (non-interface) {@link CstMethodRef} that corresponds to - * this instance. - * - * @return {@code non-null;} an appropriate instance - */ - public CstMethodRef toMethodRef() { - if (methodRef == null) { - methodRef = new CstMethodRef(getDefiningClass(), getNat()); - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "ifaceMethod"; + } - return methodRef; + /** + * Gets a normal (non-interface) {@link CstMethodRef} that corresponds to + * this instance. + * + * @return {@code non-null;} an appropriate instance + */ + public CstMethodRef toMethodRef() { + if (methodRef == null) { + methodRef = new CstMethodRef(getDefiningClass(), getNat()); } + + return methodRef; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstKnownNull.java b/dx/src/com/android/jack/dx/rop/cst/CstKnownNull.java index fe471f3..a7b6355 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstKnownNull.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstKnownNull.java @@ -22,89 +22,91 @@ import com.android.jack.dx.rop.type.Type; * Constant type to represent a known-{@code null} value. */ public final class CstKnownNull extends CstLiteralBits { - /** {@code non-null;} unique instance of this class */ - public static final CstKnownNull THE_ONE = new CstKnownNull(); + /** {@code non-null;} unique instance of this class */ + public static final CstKnownNull THE_ONE = new CstKnownNull(); - /** - * Constructs an instance. This class is not publicly instantiable. Use - * {@link #THE_ONE}. - */ - private CstKnownNull() { - // This space intentionally left blank. - } + /** + * Constructs an instance. This class is not publicly instantiable. Use + * {@link #THE_ONE}. + */ + private CstKnownNull() { + // This space intentionally left blank. + } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - return (other instanceof CstKnownNull); - } + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + return (other instanceof CstKnownNull); + } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return 0x4466757a; - } + /** {@inheritDoc} */ + @Override + public int hashCode() { + return 0x4466757a; + } - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - return 0; - } + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + return 0; + } - /** {@inheritDoc} */ - @Override - public String toString() { - return "known-null"; - } + /** {@inheritDoc} */ + @Override + public String toString() { + return "known-null"; + } - /** {@inheritDoc} */ - public Type getType() { - return Type.KNOWN_NULL; - } + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.KNOWN_NULL; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "known-null"; - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "known-null"; + } - /** {@inheritDoc} */ - @Override - public boolean isCategory2() { - return false; - } + /** {@inheritDoc} */ + @Override + public boolean isCategory2() { + return false; + } - /** {@inheritDoc} */ - public String toHuman() { - return "null"; - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return "null"; + } - /** {@inheritDoc} */ - @Override - public boolean fitsInInt() { - // See comment in getIntBits(). - return true; - } + /** {@inheritDoc} */ + @Override + public boolean fitsInInt() { + // See comment in getIntBits(). + return true; + } - /** - * {@inheritDoc} - * - * As "literal bits," a known-null is always represented as the - * number zero. - */ - @Override - public int getIntBits() { - return 0; - } + /** + * {@inheritDoc} + * + * As "literal bits," a known-null is always represented as the + * number zero. + */ + @Override + public int getIntBits() { + return 0; + } - /** - * {@inheritDoc} - * - * As "literal bits," a known-null is always represented as the - * number zero. - */ - @Override - public long getLongBits() { - return 0; - } + /** + * {@inheritDoc} + * + * As "literal bits," a known-null is always represented as the + * number zero. + */ + @Override + public long getLongBits() { + return 0; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstLiteral32.java b/dx/src/com/android/jack/dx/rop/cst/CstLiteral32.java index 488c015..8e2cec2 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstLiteral32.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstLiteral32.java @@ -19,69 +19,67 @@ package com.android.jack.dx.rop.cst; /** * Constants which are literal 32-bit values of some sort. */ -public abstract class CstLiteral32 - extends CstLiteralBits { - /** the value as {@code int} bits */ - private final int bits; +public abstract class CstLiteral32 extends CstLiteralBits { + /** the value as {@code int} bits */ + private final int bits; - /** - * Constructs an instance. - * - * @param bits the value as {@code int} bits - */ - /*package*/ CstLiteral32(int bits) { - this.bits = bits; - } + /** + * Constructs an instance. + * + * @param bits the value as {@code int} bits + */ + /*package*/CstLiteral32(int bits) { + this.bits = bits; + } - /** {@inheritDoc} */ - @Override - public final boolean equals(Object other) { - return (other != null) && - (getClass() == other.getClass()) && - bits == ((CstLiteral32) other).bits; - } + /** {@inheritDoc} */ + @Override + public final boolean equals(Object other) { + return (other != null) && (getClass() == other.getClass()) + && bits == ((CstLiteral32) other).bits; + } - /** {@inheritDoc} */ - @Override - public final int hashCode() { - return bits; - } + /** {@inheritDoc} */ + @Override + public final int hashCode() { + return bits; + } - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - int otherBits = ((CstLiteral32) other).bits; + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + int otherBits = ((CstLiteral32) other).bits; - if (bits < otherBits) { - return -1; - } else if (bits > otherBits) { - return 1; - } else { - return 0; - } + if (bits < otherBits) { + return -1; + } else if (bits > otherBits) { + return 1; + } else { + return 0; } + } - /** {@inheritDoc} */ - @Override - public final boolean isCategory2() { - return false; - } + /** {@inheritDoc} */ + @Override + public final boolean isCategory2() { + return false; + } - /** {@inheritDoc} */ - @Override - public final boolean fitsInInt() { - return true; - } + /** {@inheritDoc} */ + @Override + public final boolean fitsInInt() { + return true; + } - /** {@inheritDoc} */ - @Override - public final int getIntBits() { - return bits; - } + /** {@inheritDoc} */ + @Override + public final int getIntBits() { + return bits; + } - /** {@inheritDoc} */ - @Override - public final long getLongBits() { - return (long) bits; - } + /** {@inheritDoc} */ + @Override + public final long getLongBits() { + return bits; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstLiteral64.java b/dx/src/com/android/jack/dx/rop/cst/CstLiteral64.java index e72b089..1c3e976 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstLiteral64.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstLiteral64.java @@ -19,69 +19,67 @@ package com.android.jack.dx.rop.cst; /** * Constants which are literal 64-bit values of some sort. */ -public abstract class CstLiteral64 - extends CstLiteralBits { - /** the value as {@code long} bits */ - private final long bits; +public abstract class CstLiteral64 extends CstLiteralBits { + /** the value as {@code long} bits */ + private final long bits; - /** - * Constructs an instance. - * - * @param bits the value as {@code long} bits - */ - /*package*/ CstLiteral64(long bits) { - this.bits = bits; - } + /** + * Constructs an instance. + * + * @param bits the value as {@code long} bits + */ + /*package*/CstLiteral64(long bits) { + this.bits = bits; + } - /** {@inheritDoc} */ - @Override - public final boolean equals(Object other) { - return (other != null) && - (getClass() == other.getClass()) && - bits == ((CstLiteral64) other).bits; - } + /** {@inheritDoc} */ + @Override + public final boolean equals(Object other) { + return (other != null) && (getClass() == other.getClass()) + && bits == ((CstLiteral64) other).bits; + } - /** {@inheritDoc} */ - @Override - public final int hashCode() { - return (int) bits ^ (int) (bits >> 32); - } + /** {@inheritDoc} */ + @Override + public final int hashCode() { + return (int) bits ^ (int) (bits >> 32); + } - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - long otherBits = ((CstLiteral64) other).bits; + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + long otherBits = ((CstLiteral64) other).bits; - if (bits < otherBits) { - return -1; - } else if (bits > otherBits) { - return 1; - } else { - return 0; - } + if (bits < otherBits) { + return -1; + } else if (bits > otherBits) { + return 1; + } else { + return 0; } + } - /** {@inheritDoc} */ - @Override - public final boolean isCategory2() { - return true; - } + /** {@inheritDoc} */ + @Override + public final boolean isCategory2() { + return true; + } - /** {@inheritDoc} */ - @Override - public final boolean fitsInInt() { - return (int) bits == bits; - } + /** {@inheritDoc} */ + @Override + public final boolean fitsInInt() { + return (int) bits == bits; + } - /** {@inheritDoc} */ - @Override - public final int getIntBits() { - return (int) bits; - } + /** {@inheritDoc} */ + @Override + public final int getIntBits() { + return (int) bits; + } - /** {@inheritDoc} */ - @Override - public final long getLongBits() { - return bits; - } + /** {@inheritDoc} */ + @Override + public final long getLongBits() { + return bits; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstLiteralBits.java b/dx/src/com/android/jack/dx/rop/cst/CstLiteralBits.java index 0615aa7..4b8ead6 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstLiteralBits.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstLiteralBits.java @@ -19,64 +19,63 @@ package com.android.jack.dx.rop.cst; /** * Constants which are literal bitwise values of some sort. */ -public abstract class CstLiteralBits - extends TypedConstant { - /** - * Returns whether or not this instance's value may be accurately - * represented as an {@code int}. The rule is that if there - * is an {@code int} which may be sign-extended to yield this - * instance's value, then this method returns {@code true}. - * Otherwise, it returns {@code false}. - * - * @return {@code true} iff this instance fits in an {@code int} - */ - public abstract boolean fitsInInt(); +public abstract class CstLiteralBits extends TypedConstant { + /** + * Returns whether or not this instance's value may be accurately + * represented as an {@code int}. The rule is that if there + * is an {@code int} which may be sign-extended to yield this + * instance's value, then this method returns {@code true}. + * Otherwise, it returns {@code false}. + * + * @return {@code true} iff this instance fits in an {@code int} + */ + public abstract boolean fitsInInt(); - /** - * Gets the value as {@code int} bits. If this instance contains - * more bits than fit in an {@code int}, then this returns only - * the low-order bits. - * - * @return the bits - */ - public abstract int getIntBits(); + /** + * Gets the value as {@code int} bits. If this instance contains + * more bits than fit in an {@code int}, then this returns only + * the low-order bits. + * + * @return the bits + */ + public abstract int getIntBits(); - /** - * Gets the value as {@code long} bits. If this instance contains - * fewer bits than fit in a {@code long}, then the result of this - * method is the sign extension of the value. - * - * @return the bits - */ - public abstract long getLongBits(); + /** + * Gets the value as {@code long} bits. If this instance contains + * fewer bits than fit in a {@code long}, then the result of this + * method is the sign extension of the value. + * + * @return the bits + */ + public abstract long getLongBits(); - /** - * Returns true if this value can fit in 16 bits with sign-extension. - * - * @return true if the sign-extended lower 16 bits are the same as - * the value. - */ - public boolean fitsIn16Bits() { - if (! fitsInInt()) { - return false; - } - - int bits = getIntBits(); - return (short) bits == bits; + /** + * Returns true if this value can fit in 16 bits with sign-extension. + * + * @return true if the sign-extended lower 16 bits are the same as + * the value. + */ + public boolean fitsIn16Bits() { + if (!fitsInInt()) { + return false; } - /** - * Returns true if this value can fit in 8 bits with sign-extension. - * - * @return true if the sign-extended lower 8 bits are the same as - * the value. - */ - public boolean fitsIn8Bits() { - if (! fitsInInt()) { - return false; - } + int bits = getIntBits(); + return (short) bits == bits; + } - int bits = getIntBits(); - return (byte) bits == bits; + /** + * Returns true if this value can fit in 8 bits with sign-extension. + * + * @return true if the sign-extended lower 8 bits are the same as + * the value. + */ + public boolean fitsIn8Bits() { + if (!fitsInInt()) { + return false; } + + int bits = getIntBits(); + return (byte) bits == bits; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstLong.java b/dx/src/com/android/jack/dx/rop/cst/CstLong.java index 58c2b9b..6265566 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstLong.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstLong.java @@ -22,66 +22,67 @@ import com.android.jack.dx.util.Hex; /** * Constants of type {@code CONSTANT_Long_info}. */ -public final class CstLong - extends CstLiteral64 { - /** {@code non-null;} instance representing {@code 0} */ - public static final CstLong VALUE_0 = make(0); +public final class CstLong extends CstLiteral64 { + /** {@code non-null;} instance representing {@code 0} */ + public static final CstLong VALUE_0 = make(0); - /** {@code non-null;} instance representing {@code 1} */ - public static final CstLong VALUE_1 = make(1); + /** {@code non-null;} instance representing {@code 1} */ + public static final CstLong VALUE_1 = make(1); - /** - * Makes an instance for the given value. This may (but does not - * necessarily) return an already-allocated instance. - * - * @param value the {@code long} value + /** + * Makes an instance for the given value. This may (but does not + * necessarily) return an already-allocated instance. + * + * @param value the {@code long} value + */ + public static CstLong make(long value) { + /* + * Note: Javadoc notwithstanding, this implementation always + * allocates. */ - public static CstLong make(long value) { - /* - * Note: Javadoc notwithstanding, this implementation always - * allocates. - */ - return new CstLong(value); - } + return new CstLong(value); + } - /** - * Constructs an instance. This constructor is private; use {@link #make}. - * - * @param value the {@code long} value - */ - private CstLong(long value) { - super(value); - } + /** + * Constructs an instance. This constructor is private; use {@link #make}. + * + * @param value the {@code long} value + */ + private CstLong(long value) { + super(value); + } - /** {@inheritDoc} */ - @Override - public String toString() { - long value = getLongBits(); - return "long{0x" + Hex.u8(value) + " / " + value + '}'; - } + /** {@inheritDoc} */ + @Override + public String toString() { + long value = getLongBits(); + return "long{0x" + Hex.u8(value) + " / " + value + '}'; + } - /** {@inheritDoc} */ - public Type getType() { - return Type.LONG; - } + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.LONG; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "long"; - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "long"; + } - /** {@inheritDoc} */ - public String toHuman() { - return Long.toString(getLongBits()); - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return Long.toString(getLongBits()); + } - /** - * Gets the {@code long} value. - * - * @return the value - */ - public long getValue() { - return getLongBits(); - } + /** + * Gets the {@code long} value. + * + * @return the value + */ + public long getValue() { + return getLongBits(); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstMemberRef.java b/dx/src/com/android/jack/dx/rop/cst/CstMemberRef.java index 3415946..2b36a7d 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstMemberRef.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstMemberRef.java @@ -20,103 +20,103 @@ package com.android.jack.dx.rop.cst; * Constants of type {@code CONSTANT_*ref_info}. */ public abstract class CstMemberRef extends TypedConstant { - /** {@code non-null;} the type of the defining class */ - private final CstType definingClass; - - /** {@code non-null;} the name-and-type */ - private final CstNat nat; - - /** - * Constructs an instance. - * - * @param definingClass {@code non-null;} the type of the defining class - * @param nat {@code non-null;} the name-and-type - */ - /*package*/ CstMemberRef(CstType definingClass, CstNat nat) { - if (definingClass == null) { - throw new NullPointerException("definingClass == null"); - } - - if (nat == null) { - throw new NullPointerException("nat == null"); - } - - this.definingClass = definingClass; - this.nat = nat; + /** {@code non-null;} the type of the defining class */ + private final CstType definingClass; + + /** {@code non-null;} the name-and-type */ + private final CstNat nat; + + /** + * Constructs an instance. + * + * @param definingClass {@code non-null;} the type of the defining class + * @param nat {@code non-null;} the name-and-type + */ + /*package*/CstMemberRef(CstType definingClass, CstNat nat) { + if (definingClass == null) { + throw new NullPointerException("definingClass == null"); } - /** {@inheritDoc} */ - @Override - public final boolean equals(Object other) { - if ((other == null) || (getClass() != other.getClass())) { - return false; - } - - CstMemberRef otherRef = (CstMemberRef) other; - return definingClass.equals(otherRef.definingClass) && - nat.equals(otherRef.nat); + if (nat == null) { + throw new NullPointerException("nat == null"); } - /** {@inheritDoc} */ - @Override - public final int hashCode() { - return (definingClass.hashCode() * 31) ^ nat.hashCode(); - } + this.definingClass = definingClass; + this.nat = nat; + } - /** - * {@inheritDoc} - * - * <p><b>Note:</b> This implementation just compares the defining - * class and name, and it is up to subclasses to compare the rest - * after calling {@code super.compareTo0()}.</p> - */ - @Override - protected int compareTo0(Constant other) { - CstMemberRef otherMember = (CstMemberRef) other; - int cmp = definingClass.compareTo(otherMember.definingClass); - - if (cmp != 0) { - return cmp; - } - - CstString thisName = nat.getName(); - CstString otherName = otherMember.nat.getName(); - - return thisName.compareTo(otherName); + /** {@inheritDoc} */ + @Override + public final boolean equals(Object other) { + if ((other == null) || (getClass() != other.getClass())) { + return false; } - /** {@inheritDoc} */ - @Override - public final String toString() { - return typeName() + '{' + toHuman() + '}'; + CstMemberRef otherRef = (CstMemberRef) other; + return definingClass.equals(otherRef.definingClass) && nat.equals(otherRef.nat); + } + + /** {@inheritDoc} */ + @Override + public final int hashCode() { + return (definingClass.hashCode() * 31) ^ nat.hashCode(); + } + + /** + * {@inheritDoc} + * + * <p><b>Note:</b> This implementation just compares the defining + * class and name, and it is up to subclasses to compare the rest + * after calling {@code super.compareTo0()}.</p> + */ + @Override + protected int compareTo0(Constant other) { + CstMemberRef otherMember = (CstMemberRef) other; + int cmp = definingClass.compareTo(otherMember.definingClass); + + if (cmp != 0) { + return cmp; } - /** {@inheritDoc} */ - @Override - public final boolean isCategory2() { - return false; - } - - /** {@inheritDoc} */ - public final String toHuman() { - return definingClass.toHuman() + '.' + nat.toHuman(); - } - - /** - * Gets the type of the defining class. - * - * @return {@code non-null;} the type of defining class - */ - public final CstType getDefiningClass() { - return definingClass; - } - - /** - * Gets the defining name-and-type. - * - * @return {@code non-null;} the name-and-type - */ - public final CstNat getNat() { - return nat; - } + CstString thisName = nat.getName(); + CstString otherName = otherMember.nat.getName(); + + return thisName.compareTo(otherName); + } + + /** {@inheritDoc} */ + @Override + public final String toString() { + return typeName() + '{' + toHuman() + '}'; + } + + /** {@inheritDoc} */ + @Override + public final boolean isCategory2() { + return false; + } + + /** {@inheritDoc} */ + @Override + public final String toHuman() { + return definingClass.toHuman() + '.' + nat.toHuman(); + } + + /** + * Gets the type of the defining class. + * + * @return {@code non-null;} the type of defining class + */ + public final CstType getDefiningClass() { + return definingClass; + } + + /** + * Gets the defining name-and-type. + * + * @return {@code non-null;} the name-and-type + */ + public final CstNat getNat() { + return nat; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstMethodRef.java b/dx/src/com/android/jack/dx/rop/cst/CstMethodRef.java index 3b6d2a4..f28e050 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstMethodRef.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstMethodRef.java @@ -19,21 +19,20 @@ package com.android.jack.dx.rop.cst; /** * Constants of type {@code CONSTANT_Methodref_info}. */ -public final class CstMethodRef - extends CstBaseMethodRef { - /** - * Constructs an instance. - * - * @param definingClass {@code non-null;} the type of the defining class - * @param nat {@code non-null;} the name-and-type - */ - public CstMethodRef(CstType definingClass, CstNat nat) { - super(definingClass, nat); - } +public final class CstMethodRef extends CstBaseMethodRef { + /** + * Constructs an instance. + * + * @param definingClass {@code non-null;} the type of the defining class + * @param nat {@code non-null;} the name-and-type + */ + public CstMethodRef(CstType definingClass, CstNat nat) { + super(definingClass, nat); + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "method"; - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "method"; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstNat.java b/dx/src/com/android/jack/dx/rop/cst/CstNat.java index 75c521e..6abdbb7 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstNat.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstNat.java @@ -22,149 +22,148 @@ import com.android.jack.dx.rop.type.Type; * Constants of type {@code CONSTANT_NameAndType_info}. */ public final class CstNat extends Constant { - /** - * {@code non-null;} the instance for name {@code TYPE} and descriptor - * {@code java.lang.Class}, which is useful when dealing with - * wrapped primitives - */ - public static final CstNat PRIMITIVE_TYPE_NAT = - new CstNat(new CstString("TYPE"), - new CstString("Ljava/lang/Class;")); - - /** {@code non-null;} the name */ - private final CstString name; - - /** {@code non-null;} the descriptor (type) */ - private final CstString descriptor; - - /** - * Constructs an instance. - * - * @param name {@code non-null;} the name - * @param descriptor {@code non-null;} the descriptor - */ - public CstNat(CstString name, CstString descriptor) { - if (name == null) { - throw new NullPointerException("name == null"); - } - - if (descriptor == null) { - throw new NullPointerException("descriptor == null"); - } - - this.name = name; - this.descriptor = descriptor; + /** + * {@code non-null;} the instance for name {@code TYPE} and descriptor + * {@code java.lang.Class}, which is useful when dealing with + * wrapped primitives + */ + public static final CstNat PRIMITIVE_TYPE_NAT = + new CstNat(new CstString("TYPE"), new CstString("Ljava/lang/Class;")); + + /** {@code non-null;} the name */ + private final CstString name; + + /** {@code non-null;} the descriptor (type) */ + private final CstString descriptor; + + /** + * Constructs an instance. + * + * @param name {@code non-null;} the name + * @param descriptor {@code non-null;} the descriptor + */ + public CstNat(CstString name, CstString descriptor) { + if (name == null) { + throw new NullPointerException("name == null"); } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof CstNat)) { - return false; - } - - CstNat otherNat = (CstNat) other; - return name.equals(otherNat.name) && - descriptor.equals(otherNat.descriptor); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return (name.hashCode() * 31) ^ descriptor.hashCode(); - } - - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - CstNat otherNat = (CstNat) other; - int cmp = name.compareTo(otherNat.name); - - if (cmp != 0) { - return cmp; - } - - return descriptor.compareTo(otherNat.descriptor); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return "nat{" + toHuman() + '}'; - } - - /** {@inheritDoc} */ - @Override - public String typeName() { - return "nat"; + if (descriptor == null) { + throw new NullPointerException("descriptor == null"); } - /** {@inheritDoc} */ - @Override - public boolean isCategory2() { - return false; - } + this.name = name; + this.descriptor = descriptor; + } - /** - * Gets the name. - * - * @return {@code non-null;} the name - */ - public CstString getName() { - return name; + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof CstNat)) { + return false; } - /** - * Gets the descriptor. - * - * @return {@code non-null;} the descriptor - */ - public CstString getDescriptor() { - return descriptor; - } + CstNat otherNat = (CstNat) other; + return name.equals(otherNat.name) && descriptor.equals(otherNat.descriptor); + } - /** - * Returns an unadorned but human-readable version of the name-and-type - * value. - * - * @return {@code non-null;} the human form - */ - public String toHuman() { - return name.toHuman() + ':' + descriptor.toHuman(); - } + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (name.hashCode() * 31) ^ descriptor.hashCode(); + } - /** - * Gets the field type corresponding to this instance's descriptor. - * This method is only valid to call if the descriptor in fact describes - * a field (and not a method). - * - * @return {@code non-null;} the field type - */ - public Type getFieldType() { - return Type.intern(descriptor.getString()); - } + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + CstNat otherNat = (CstNat) other; + int cmp = name.compareTo(otherNat.name); - /** - * Gets whether this instance has the name of a standard instance - * initialization method. This is just a convenient shorthand for - * {@code getName().getString().equals("<init>")}. - * - * @return {@code true} iff this is a reference to an - * instance initialization method - */ - public final boolean isInstanceInit() { - return name.getString().equals("<init>"); + if (cmp != 0) { + return cmp; } - /** - * Gets whether this instance has the name of a standard class - * initialization method. This is just a convenient shorthand for - * {@code getName().getString().equals("<clinit>")}. - * - * @return {@code true} iff this is a reference to an - * instance initialization method - */ - public final boolean isClassInit() { - return name.getString().equals("<clinit>"); - } + return descriptor.compareTo(otherNat.descriptor); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "nat{" + toHuman() + '}'; + } + + /** {@inheritDoc} */ + @Override + public String typeName() { + return "nat"; + } + + /** {@inheritDoc} */ + @Override + public boolean isCategory2() { + return false; + } + + /** + * Gets the name. + * + * @return {@code non-null;} the name + */ + public CstString getName() { + return name; + } + + /** + * Gets the descriptor. + * + * @return {@code non-null;} the descriptor + */ + public CstString getDescriptor() { + return descriptor; + } + + /** + * Returns an unadorned but human-readable version of the name-and-type + * value. + * + * @return {@code non-null;} the human form + */ + @Override + public String toHuman() { + return name.toHuman() + ':' + descriptor.toHuman(); + } + + /** + * Gets the field type corresponding to this instance's descriptor. + * This method is only valid to call if the descriptor in fact describes + * a field (and not a method). + * + * @return {@code non-null;} the field type + */ + public Type getFieldType() { + return Type.intern(descriptor.getString()); + } + + /** + * Gets whether this instance has the name of a standard instance + * initialization method. This is just a convenient shorthand for + * {@code getName().getString().equals("<init>")}. + * + * @return {@code true} iff this is a reference to an + * instance initialization method + */ + public final boolean isInstanceInit() { + return name.getString().equals("<init>"); + } + + /** + * Gets whether this instance has the name of a standard class + * initialization method. This is just a convenient shorthand for + * {@code getName().getString().equals("<clinit>")}. + * + * @return {@code true} iff this is a reference to an + * instance initialization method + */ + public final boolean isClassInit() { + return name.getString().equals("<clinit>"); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstShort.java b/dx/src/com/android/jack/dx/rop/cst/CstShort.java index 688ae6d..3038c91 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstShort.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstShort.java @@ -22,79 +22,79 @@ import com.android.jack.dx.util.Hex; /** * Constants of type {@code short}. */ -public final class CstShort - extends CstLiteral32 { - /** {@code non-null;} the value {@code 0} as an instance of this class */ - public static final CstShort VALUE_0 = make((short) 0); +public final class CstShort extends CstLiteral32 { + /** {@code non-null;} the value {@code 0} as an instance of this class */ + public static final CstShort VALUE_0 = make((short) 0); - /** - * Makes an instance for the given value. This may (but does not - * necessarily) return an already-allocated instance. - * - * @param value the {@code short} value - * @return {@code non-null;} the appropriate instance - */ - public static CstShort make(short value) { - return new CstShort(value); - } - - /** - * Makes an instance for the given {@code int} value. This - * may (but does not necessarily) return an already-allocated - * instance. - * - * @param value the value, which must be in range for a {@code short} - * @return {@code non-null;} the appropriate instance - */ - public static CstShort make(int value) { - short cast = (short) value; + /** + * Makes an instance for the given value. This may (but does not + * necessarily) return an already-allocated instance. + * + * @param value the {@code short} value + * @return {@code non-null;} the appropriate instance + */ + public static CstShort make(short value) { + return new CstShort(value); + } - if (cast != value) { - throw new IllegalArgumentException("bogus short value: " + - value); - } + /** + * Makes an instance for the given {@code int} value. This + * may (but does not necessarily) return an already-allocated + * instance. + * + * @param value the value, which must be in range for a {@code short} + * @return {@code non-null;} the appropriate instance + */ + public static CstShort make(int value) { + short cast = (short) value; - return make(cast); + if (cast != value) { + throw new IllegalArgumentException("bogus short value: " + value); } - /** - * Constructs an instance. This constructor is private; use {@link #make}. - * - * @param value the {@code short} value - */ - private CstShort(short value) { - super(value); - } + return make(cast); + } - /** {@inheritDoc} */ - @Override - public String toString() { - int value = getIntBits(); - return "short{0x" + Hex.u2(value) + " / " + value + '}'; - } + /** + * Constructs an instance. This constructor is private; use {@link #make}. + * + * @param value the {@code short} value + */ + private CstShort(short value) { + super(value); + } - /** {@inheritDoc} */ - public Type getType() { - return Type.SHORT; - } + /** {@inheritDoc} */ + @Override + public String toString() { + int value = getIntBits(); + return "short{0x" + Hex.u2(value) + " / " + value + '}'; + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "short"; - } + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.SHORT; + } - /** {@inheritDoc} */ - public String toHuman() { - return Integer.toString(getIntBits()); - } + /** {@inheritDoc} */ + @Override + public String typeName() { + return "short"; + } - /** - * Gets the {@code short} value. - * - * @return the value - */ - public short getValue() { - return (short) getIntBits(); - } + /** {@inheritDoc} */ + @Override + public String toHuman() { + return Integer.toString(getIntBits()); + } + + /** + * Gets the {@code short} value. + * + * @return the value + */ + public short getValue() { + return (short) getIntBits(); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstString.java b/dx/src/com/android/jack/dx/rop/cst/CstString.java index 42cf62d..94d547c 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstString.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstString.java @@ -24,352 +24,364 @@ import com.android.jack.dx.util.Hex; * Constants of type {@code CONSTANT_Utf8_info} or {@code CONSTANT_String_info}. */ public final class CstString extends TypedConstant { - /** - * {@code non-null;} instance representing {@code ""}, that is, the - * empty string - */ - public static final CstString EMPTY_STRING = new CstString(""); - - /** {@code non-null;} the UTF-8 value as a string */ - private final String string; - - /** {@code non-null;} the UTF-8 value as bytes */ - private final ByteArray bytes; - - /** - * Converts a string into its MUTF-8 form. MUTF-8 differs from normal UTF-8 - * in the handling of character '\0' and surrogate pairs. - * - * @param string {@code non-null;} the string to convert - * @return {@code 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; + /** + * {@code non-null;} instance representing {@code ""}, that is, the + * empty string + */ + public static final CstString EMPTY_STRING = new CstString(""); + + /** {@code non-null;} the UTF-8 value as a string */ + private final String string; + + /** {@code non-null;} the UTF-8 value as bytes */ + private final ByteArray bytes; + + /** + * Converts a string into its MUTF-8 form. MUTF-8 differs from normal UTF-8 + * in the handling of character '\0' and surrogate pairs. + * + * @param string {@code non-null;} the string to convert + * @return {@code 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; + } } - /** - * Converts an array of UTF-8 bytes into a string. - * - * @param bytes {@code non-null;} the bytes to convert - * @return {@code non-null;} the converted string - */ - public static String utf8BytesToString(ByteArray bytes) { - int length = bytes.size(); - char[] chars = new char[length]; // This is sized to avoid a realloc. - int outAt = 0; - - for (int at = 0; length > 0; /*at*/) { - int v0 = bytes.getUnsignedByte(at); - char out; - switch (v0 >> 4) { - case 0x00: case 0x01: case 0x02: case 0x03: - case 0x04: case 0x05: case 0x06: case 0x07: { - // 0XXXXXXX -- single-byte encoding - length--; - if (v0 == 0) { - // A single zero byte is illegal. - return throwBadUtf8(v0, at); - } - out = (char) v0; - at++; - break; - } - case 0x0c: case 0x0d: { - // 110XXXXX -- two-byte encoding - length -= 2; - if (length < 0) { - return throwBadUtf8(v0, at); - } - int v1 = bytes.getUnsignedByte(at + 1); - if ((v1 & 0xc0) != 0x80) { - return throwBadUtf8(v1, at + 1); - } - int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f); - if ((value != 0) && (value < 0x80)) { - /* - * This should have been represented with - * one-byte encoding. - */ - return throwBadUtf8(v1, at + 1); - } - out = (char) value; - at += 2; - break; - } - case 0x0e: { - // 1110XXXX -- three-byte encoding - length -= 3; - if (length < 0) { - return throwBadUtf8(v0, at); - } - int v1 = bytes.getUnsignedByte(at + 1); - if ((v1 & 0xc0) != 0x80) { - return throwBadUtf8(v1, at + 1); - } - int v2 = bytes.getUnsignedByte(at + 2); - if ((v1 & 0xc0) != 0x80) { - return throwBadUtf8(v2, at + 2); - } - int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) | - (v2 & 0x3f); - if (value < 0x800) { - /* - * This should have been represented with one- or - * two-byte encoding. - */ - return throwBadUtf8(v2, at + 2); - } - out = (char) value; - at += 3; - break; - } - default: { - // 10XXXXXX, 1111XXXX -- illegal - return throwBadUtf8(v0, at); - } - } - chars[outAt] = out; - outAt++; + byte[] result = new byte[outAt]; + System.arraycopy(bytes, 0, result, 0, outAt); + return result; + } + + /** + * Converts an array of UTF-8 bytes into a string. + * + * @param bytes {@code non-null;} the bytes to convert + * @return {@code non-null;} the converted string + */ + public static String utf8BytesToString(ByteArray bytes) { + int length = bytes.size(); + char[] chars = new char[length]; // This is sized to avoid a realloc. + int outAt = 0; + + for (int at = 0; length > 0; /*at*/) { + int v0 = bytes.getUnsignedByte(at); + char out; + switch (v0 >> 4) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: { + // 0XXXXXXX -- single-byte encoding + length--; + if (v0 == 0) { + // A single zero byte is illegal. + return throwBadUtf8(v0, at); + } + out = (char) v0; + at++; + break; } - - return new String(chars, 0, outAt); - } - - /** - * Helper for {@link #utf8BytesToString}, which throws the right - * exception for a bogus utf-8 byte. - * - * @param value the byte value - * @param offset the file offset - * @return never - * @throws IllegalArgumentException always thrown - */ - private static String throwBadUtf8(int value, int offset) { - throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) + - " at offset " + Hex.u4(offset)); - } - - /** - * Constructs an instance from a {@code String}. - * - * @param string {@code non-null;} the UTF-8 value as a string - */ - public CstString(String string) { - if (string == null) { - throw new NullPointerException("string == null"); + case 0x0c: + case 0x0d: { + // 110XXXXX -- two-byte encoding + length -= 2; + if (length < 0) { + return throwBadUtf8(v0, at); + } + int v1 = bytes.getUnsignedByte(at + 1); + if ((v1 & 0xc0) != 0x80) { + return throwBadUtf8(v1, at + 1); + } + int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f); + if ((value != 0) && (value < 0x80)) { + /* + * This should have been represented with + * one-byte encoding. + */ + return throwBadUtf8(v1, at + 1); + } + out = (char) value; + at += 2; + break; } - - this.string = string.intern(); - this.bytes = new ByteArray(stringToUtf8Bytes(string)); - } - - /** - * Constructs an instance from some UTF-8 bytes. - * - * @param bytes {@code non-null;} array of the UTF-8 bytes - */ - public CstString(ByteArray bytes) { - if (bytes == null) { - throw new NullPointerException("bytes == null"); + case 0x0e: { + // 1110XXXX -- three-byte encoding + length -= 3; + if (length < 0) { + return throwBadUtf8(v0, at); + } + int v1 = bytes.getUnsignedByte(at + 1); + if ((v1 & 0xc0) != 0x80) { + return throwBadUtf8(v1, at + 1); + } + int v2 = bytes.getUnsignedByte(at + 2); + if ((v1 & 0xc0) != 0x80) { + return throwBadUtf8(v2, at + 2); + } + int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) | (v2 & 0x3f); + if (value < 0x800) { + /* + * This should have been represented with one- or + * two-byte encoding. + */ + return throwBadUtf8(v2, at + 2); + } + out = (char) value; + at += 3; + break; } - - this.bytes = bytes; - this.string = utf8BytesToString(bytes).intern(); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof CstString)) { - return false; + default: { + // 10XXXXXX, 1111XXXX -- illegal + return throwBadUtf8(v0, at); } - - return string.equals(((CstString) other).string); + } + chars[outAt] = out; + outAt++; } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return string.hashCode(); + return new String(chars, 0, outAt); + } + + /** + * Helper for {@link #utf8BytesToString}, which throws the right + * exception for a bogus utf-8 byte. + * + * @param value the byte value + * @param offset the file offset + * @return never + * @throws IllegalArgumentException always thrown + */ + private static String throwBadUtf8(int value, int offset) { + throw new IllegalArgumentException( + "bad utf-8 byte " + Hex.u1(value) + " at offset " + Hex.u4(offset)); + } + + /** + * Constructs an instance from a {@code String}. + * + * @param string {@code non-null;} the UTF-8 value as a string + */ + public CstString(String string) { + if (string == null) { + throw new NullPointerException("string == null"); } - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - return string.compareTo(((CstString) other).string); + this.string = string.intern(); + this.bytes = new ByteArray(stringToUtf8Bytes(string)); + } + + /** + * Constructs an instance from some UTF-8 bytes. + * + * @param bytes {@code non-null;} array of the UTF-8 bytes + */ + public CstString(ByteArray bytes) { + if (bytes == null) { + throw new NullPointerException("bytes == null"); } - /** {@inheritDoc} */ - @Override - public String toString() { - return "string{\"" + toHuman() + "\"}"; - } + this.bytes = bytes; + this.string = utf8BytesToString(bytes).intern(); + } - /** {@inheritDoc} */ - @Override - public String typeName() { - return "utf8"; + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof CstString)) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean isCategory2() { - return false; - } - - /** {@inheritDoc} */ - public String toHuman() { - int len = string.length(); - StringBuilder sb = new StringBuilder(len * 3 / 2); - - for (int i = 0; i < len; i++) { - char c = string.charAt(i); - if ((c >= ' ') && (c < 0x7f)) { - if ((c == '\'') || (c == '\"') || (c == '\\')) { - sb.append('\\'); - } - sb.append(c); - } else if (c <= 0x7f) { - switch (c) { - case '\n': sb.append("\\n"); break; - case '\r': sb.append("\\r"); break; - case '\t': sb.append("\\t"); break; - default: { - /* - * Represent the character as an octal escape. - * If the next character is a valid octal - * digit, disambiguate by using the - * three-digit form. - */ - char nextChar = - (i < (len - 1)) ? string.charAt(i + 1) : 0; - boolean displayZero = - (nextChar >= '0') && (nextChar <= '7'); - sb.append('\\'); - for (int shift = 6; shift >= 0; shift -= 3) { - char outChar = (char) (((c >> shift) & 7) + '0'); - if ((outChar != '0') || displayZero) { - sb.append(outChar); - displayZero = true; - } - } - if (! displayZero) { - // Ironic edge case: The original value was 0. - sb.append('0'); - } - break; - } - } - } else { - sb.append("\\u"); - sb.append(Character.forDigit(c >> 12, 16)); - sb.append(Character.forDigit((c >> 8) & 0x0f, 16)); - sb.append(Character.forDigit((c >> 4) & 0x0f, 16)); - sb.append(Character.forDigit(c & 0x0f, 16)); + return string.equals(((CstString) other).string); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return string.hashCode(); + } + + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + return string.compareTo(((CstString) other).string); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "string{\"" + toHuman() + "\"}"; + } + + /** {@inheritDoc} */ + @Override + public String typeName() { + return "utf8"; + } + + /** {@inheritDoc} */ + @Override + public boolean isCategory2() { + return false; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + int len = string.length(); + StringBuilder sb = new StringBuilder(len * 3 / 2); + + for (int i = 0; i < len; i++) { + char c = string.charAt(i); + if ((c >= ' ') && (c < 0x7f)) { + if ((c == '\'') || (c == '\"') || (c == '\\')) { + sb.append('\\'); + } + sb.append(c); + } else if (c <= 0x7f) { + switch (c) { + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + default: { + /* + * Represent the character as an octal escape. + * If the next character is a valid octal + * digit, disambiguate by using the + * three-digit form. + */ + char nextChar = (i < (len - 1)) ? string.charAt(i + 1) : 0; + boolean displayZero = (nextChar >= '0') && (nextChar <= '7'); + sb.append('\\'); + for (int shift = 6; shift >= 0; shift -= 3) { + char outChar = (char) (((c >> shift) & 7) + '0'); + if ((outChar != '0') || displayZero) { + sb.append(outChar); + displayZero = true; + } + } + if (!displayZero) { + // Ironic edge case: The original value was 0. + sb.append('0'); } + break; + } } - - return sb.toString(); + } else { + sb.append("\\u"); + sb.append(Character.forDigit(c >> 12, 16)); + sb.append(Character.forDigit((c >> 8) & 0x0f, 16)); + sb.append(Character.forDigit((c >> 4) & 0x0f, 16)); + sb.append(Character.forDigit(c & 0x0f, 16)); + } } - /** - * Gets the value as a human-oriented string, surrounded by double - * quotes. - * - * @return {@code non-null;} the quoted string - */ - public String toQuoted() { - return '\"' + toHuman() + '\"'; + return sb.toString(); + } + + /** + * Gets the value as a human-oriented string, surrounded by double + * quotes. + * + * @return {@code non-null;} the quoted string + */ + public String toQuoted() { + return '\"' + toHuman() + '\"'; + } + + /** + * Gets the value as a human-oriented string, surrounded by double + * quotes, but ellipsizes the result if it is longer than the given + * maximum length + * + * @param maxLength {@code >= 5;} the maximum length of the string to return + * @return {@code non-null;} the quoted string + */ + public String toQuoted(int maxLength) { + String string = toHuman(); + int length = string.length(); + String ellipses; + + if (length <= (maxLength - 2)) { + ellipses = ""; + } else { + string = string.substring(0, maxLength - 5); + ellipses = "..."; } - /** - * Gets the value as a human-oriented string, surrounded by double - * quotes, but ellipsizes the result if it is longer than the given - * maximum length - * - * @param maxLength {@code >= 5;} the maximum length of the string to return - * @return {@code non-null;} the quoted string - */ - public String toQuoted(int maxLength) { - String string = toHuman(); - int length = string.length(); - String ellipses; - - if (length <= (maxLength - 2)) { - ellipses = ""; - } else { - string = string.substring(0, maxLength - 5); - ellipses = "..."; - } - - return '\"' + string + ellipses + '\"'; - } - - /** - * Gets the UTF-8 value as a string. - * The returned string is always already interned. - * - * @return {@code non-null;} the UTF-8 value as a string - */ - public String getString() { - return string; - } - - /** - * Gets the UTF-8 value as UTF-8 encoded bytes. - * - * @return {@code non-null;} an array of the UTF-8 bytes - */ - public ByteArray getBytes() { - return bytes; - } - - /** - * Gets the size of this instance as UTF-8 code points. That is, - * get the number of bytes in the UTF-8 encoding of this instance. - * - * @return {@code >= 0;} the UTF-8 size - */ - public int getUtf8Size() { - return bytes.size(); - } - - /** - * Gets the size of this instance as UTF-16 code points. That is, - * get the number of 16-bit chars in the UTF-16 encoding of this - * instance. This is the same as the {@code length} of the - * Java {@code String} representation of this instance. - * - * @return {@code >= 0;} the UTF-16 size - */ - public int getUtf16Size() { - return string.length(); - } - - public Type getType() { - return Type.STRING; - } + return '\"' + string + ellipses + '\"'; + } + + /** + * Gets the UTF-8 value as a string. + * The returned string is always already interned. + * + * @return {@code non-null;} the UTF-8 value as a string + */ + public String getString() { + return string; + } + + /** + * Gets the UTF-8 value as UTF-8 encoded bytes. + * + * @return {@code non-null;} an array of the UTF-8 bytes + */ + public ByteArray getBytes() { + return bytes; + } + + /** + * Gets the size of this instance as UTF-8 code points. That is, + * get the number of bytes in the UTF-8 encoding of this instance. + * + * @return {@code >= 0;} the UTF-8 size + */ + public int getUtf8Size() { + return bytes.size(); + } + + /** + * Gets the size of this instance as UTF-16 code points. That is, + * get the number of 16-bit chars in the UTF-16 encoding of this + * instance. This is the same as the {@code length} of the + * Java {@code String} representation of this instance. + * + * @return {@code >= 0;} the UTF-16 size + */ + public int getUtf16Size() { + return string.length(); + } + + @Override + public Type getType() { + return Type.STRING; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/CstType.java b/dx/src/com/android/jack/dx/rop/cst/CstType.java index 26d5a07..4ef8ad5 100644 --- a/dx/src/com/android/jack/dx/rop/cst/CstType.java +++ b/dx/src/com/android/jack/dx/rop/cst/CstType.java @@ -24,227 +24,236 @@ import java.util.HashMap; * Constants that represent an arbitrary type (reference or primitive). */ public final class CstType extends TypedConstant { - /** {@code non-null;} map of interned types */ - private static final HashMap<Type, CstType> interns = - new HashMap<Type, CstType>(100); - - /** {@code non-null;} instance corresponding to the class {@code Object} */ - public static final CstType OBJECT = intern(Type.OBJECT); - - /** {@code non-null;} instance corresponding to the class {@code Boolean} */ - public static final CstType BOOLEAN = intern(Type.BOOLEAN_CLASS); - - /** {@code non-null;} instance corresponding to the class {@code Byte} */ - public static final CstType BYTE = intern(Type.BYTE_CLASS); - - /** {@code non-null;} instance corresponding to the class {@code Character} */ - public static final CstType CHARACTER = intern(Type.CHARACTER_CLASS); - - /** {@code non-null;} instance corresponding to the class {@code Double} */ - public static final CstType DOUBLE = intern(Type.DOUBLE_CLASS); - - /** {@code non-null;} instance corresponding to the class {@code Float} */ - public static final CstType FLOAT = intern(Type.FLOAT_CLASS); - - /** {@code non-null;} instance corresponding to the class {@code Long} */ - public static final CstType LONG = intern(Type.LONG_CLASS); - - /** {@code non-null;} instance corresponding to the class {@code Integer} */ - public static final CstType INTEGER = intern(Type.INTEGER_CLASS); - - /** {@code non-null;} instance corresponding to the class {@code Short} */ - public static final CstType SHORT = intern(Type.SHORT_CLASS); - - /** {@code non-null;} instance corresponding to the class {@code Void} */ - public static final CstType VOID = intern(Type.VOID_CLASS); - - /** {@code non-null;} instance corresponding to the type {@code boolean[]} */ - public static final CstType BOOLEAN_ARRAY = intern(Type.BOOLEAN_ARRAY); - - /** {@code non-null;} instance corresponding to the type {@code byte[]} */ - public static final CstType BYTE_ARRAY = intern(Type.BYTE_ARRAY); - - /** {@code non-null;} instance corresponding to the type {@code char[]} */ - public static final CstType CHAR_ARRAY = intern(Type.CHAR_ARRAY); - - /** {@code non-null;} instance corresponding to the type {@code double[]} */ - public static final CstType DOUBLE_ARRAY = intern(Type.DOUBLE_ARRAY); - - /** {@code non-null;} instance corresponding to the type {@code float[]} */ - public static final CstType FLOAT_ARRAY = intern(Type.FLOAT_ARRAY); - - /** {@code non-null;} instance corresponding to the type {@code long[]} */ - public static final CstType LONG_ARRAY = intern(Type.LONG_ARRAY); - - /** {@code non-null;} instance corresponding to the type {@code int[]} */ - public static final CstType INT_ARRAY = intern(Type.INT_ARRAY); - - /** {@code non-null;} instance corresponding to the type {@code short[]} */ - public static final CstType SHORT_ARRAY = intern(Type.SHORT_ARRAY); - - /** {@code non-null;} the underlying type */ - private final Type type; - - /** - * {@code null-ok;} the type descriptor corresponding to this instance, if - * calculated - */ - private CstString descriptor; - - /** - * Returns an instance of this class that represents the wrapper - * class corresponding to a given primitive type. For example, if - * given {@link Type#INT}, this method returns the class reference - * {@code java.lang.Integer}. - * - * @param primitiveType {@code non-null;} the primitive type - * @return {@code non-null;} the corresponding wrapper class - */ - public static CstType forBoxedPrimitiveType(Type primitiveType) { - switch (primitiveType.getBasicType()) { - case Type.BT_BOOLEAN: return BOOLEAN; - case Type.BT_BYTE: return BYTE; - case Type.BT_CHAR: return CHARACTER; - case Type.BT_DOUBLE: return DOUBLE; - case Type.BT_FLOAT: return FLOAT; - case Type.BT_INT: return INTEGER; - case Type.BT_LONG: return LONG; - case Type.BT_SHORT: return SHORT; - case Type.BT_VOID: return VOID; - } - - throw new IllegalArgumentException("not primitive: " + primitiveType); - } - - /** - * Returns an interned instance of this class for the given type. - * - * @param type {@code non-null;} the underlying type - * @return {@code non-null;} an appropriately-constructed instance - */ - public static CstType intern(Type type) { - synchronized (interns) { - CstType cst = interns.get(type); - - if (cst == null) { - cst = new CstType(type); - interns.put(type, cst); - } - - return cst; - } - } - - /** - * Constructs an instance. - * - * @param type {@code non-null;} the underlying type - */ - public CstType(Type type) { - if (type == null) { - throw new NullPointerException("type == null"); - } - - if (type == type.KNOWN_NULL) { - throw new UnsupportedOperationException( - "KNOWN_NULL is not representable"); - } - - this.type = type; - this.descriptor = null; - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (!(other instanceof CstType)) { - return false; - } - - return type == ((CstType) other).type; + /** {@code non-null;} map of interned types */ + private static final HashMap<Type, CstType> interns = new HashMap<Type, CstType>(100); + + /** {@code non-null;} instance corresponding to the class {@code Object} */ + public static final CstType OBJECT = intern(Type.OBJECT); + + /** {@code non-null;} instance corresponding to the class {@code Boolean} */ + public static final CstType BOOLEAN = intern(Type.BOOLEAN_CLASS); + + /** {@code non-null;} instance corresponding to the class {@code Byte} */ + public static final CstType BYTE = intern(Type.BYTE_CLASS); + + /** {@code non-null;} instance corresponding to the class {@code Character} */ + public static final CstType CHARACTER = intern(Type.CHARACTER_CLASS); + + /** {@code non-null;} instance corresponding to the class {@code Double} */ + public static final CstType DOUBLE = intern(Type.DOUBLE_CLASS); + + /** {@code non-null;} instance corresponding to the class {@code Float} */ + public static final CstType FLOAT = intern(Type.FLOAT_CLASS); + + /** {@code non-null;} instance corresponding to the class {@code Long} */ + public static final CstType LONG = intern(Type.LONG_CLASS); + + /** {@code non-null;} instance corresponding to the class {@code Integer} */ + public static final CstType INTEGER = intern(Type.INTEGER_CLASS); + + /** {@code non-null;} instance corresponding to the class {@code Short} */ + public static final CstType SHORT = intern(Type.SHORT_CLASS); + + /** {@code non-null;} instance corresponding to the class {@code Void} */ + public static final CstType VOID = intern(Type.VOID_CLASS); + + /** {@code non-null;} instance corresponding to the type {@code boolean[]} */ + public static final CstType BOOLEAN_ARRAY = intern(Type.BOOLEAN_ARRAY); + + /** {@code non-null;} instance corresponding to the type {@code byte[]} */ + public static final CstType BYTE_ARRAY = intern(Type.BYTE_ARRAY); + + /** {@code non-null;} instance corresponding to the type {@code char[]} */ + public static final CstType CHAR_ARRAY = intern(Type.CHAR_ARRAY); + + /** {@code non-null;} instance corresponding to the type {@code double[]} */ + public static final CstType DOUBLE_ARRAY = intern(Type.DOUBLE_ARRAY); + + /** {@code non-null;} instance corresponding to the type {@code float[]} */ + public static final CstType FLOAT_ARRAY = intern(Type.FLOAT_ARRAY); + + /** {@code non-null;} instance corresponding to the type {@code long[]} */ + public static final CstType LONG_ARRAY = intern(Type.LONG_ARRAY); + + /** {@code non-null;} instance corresponding to the type {@code int[]} */ + public static final CstType INT_ARRAY = intern(Type.INT_ARRAY); + + /** {@code non-null;} instance corresponding to the type {@code short[]} */ + public static final CstType SHORT_ARRAY = intern(Type.SHORT_ARRAY); + + /** {@code non-null;} the underlying type */ + private final Type type; + + /** + * {@code null-ok;} the type descriptor corresponding to this instance, if + * calculated + */ + private CstString descriptor; + + /** + * Returns an instance of this class that represents the wrapper + * class corresponding to a given primitive type. For example, if + * given {@link Type#INT}, this method returns the class reference + * {@code java.lang.Integer}. + * + * @param primitiveType {@code non-null;} the primitive type + * @return {@code non-null;} the corresponding wrapper class + */ + public static CstType forBoxedPrimitiveType(Type primitiveType) { + switch (primitiveType.getBasicType()) { + case Type.BT_BOOLEAN: + return BOOLEAN; + case Type.BT_BYTE: + return BYTE; + case Type.BT_CHAR: + return CHARACTER; + case Type.BT_DOUBLE: + return DOUBLE; + case Type.BT_FLOAT: + return FLOAT; + case Type.BT_INT: + return INTEGER; + case Type.BT_LONG: + return LONG; + case Type.BT_SHORT: + return SHORT; + case Type.BT_VOID: + return VOID; } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return type.hashCode(); + throw new IllegalArgumentException("not primitive: " + primitiveType); + } + + /** + * Returns an interned instance of this class for the given type. + * + * @param type {@code non-null;} the underlying type + * @return {@code non-null;} an appropriately-constructed instance + */ + public static CstType intern(Type type) { + synchronized (interns) { + CstType cst = interns.get(type); + + if (cst == null) { + cst = new CstType(type); + interns.put(type, cst); + } + + return cst; } - - /** {@inheritDoc} */ - @Override - protected int compareTo0(Constant other) { - String thisDescriptor = type.getDescriptor(); - String otherDescriptor = ((CstType) other).type.getDescriptor(); - return thisDescriptor.compareTo(otherDescriptor); + } + + /** + * Constructs an instance. + * + * @param type {@code non-null;} the underlying type + */ + public CstType(Type type) { + if (type == null) { + throw new NullPointerException("type == null"); } - /** {@inheritDoc} */ - @Override - public String toString() { - return "type{" + toHuman() + '}'; + if (type == Type.KNOWN_NULL) { + throw new UnsupportedOperationException("KNOWN_NULL is not representable"); } - /** {@inheritDoc} */ - public Type getType() { - return Type.CLASS; - } - - /** {@inheritDoc} */ - @Override - public String typeName() { - return "type"; - } - - /** {@inheritDoc} */ - @Override - public boolean isCategory2() { - return false; - } - - /** {@inheritDoc} */ - public String toHuman() { - return type.toHuman(); - } + this.type = type; + this.descriptor = null; + } - /** - * Gets the underlying type (as opposed to the type corresponding - * to this instance as a constant, which is always - * {@code Class}). - * - * @return {@code non-null;} the type corresponding to the name - */ - public Type getClassType() { - return type; + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (!(other instanceof CstType)) { + return false; } - /** - * Gets the type descriptor for this instance. - * - * @return {@code non-null;} the descriptor - */ - public CstString getDescriptor() { - if (descriptor == null) { - descriptor = new CstString(type.getDescriptor()); - } - - return descriptor; + return type == ((CstType) other).type; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return type.hashCode(); + } + + /** {@inheritDoc} */ + @Override + protected int compareTo0(Constant other) { + String thisDescriptor = type.getDescriptor(); + String otherDescriptor = ((CstType) other).type.getDescriptor(); + return thisDescriptor.compareTo(otherDescriptor); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "type{" + toHuman() + '}'; + } + + /** {@inheritDoc} */ + @Override + public Type getType() { + return Type.CLASS; + } + + /** {@inheritDoc} */ + @Override + public String typeName() { + return "type"; + } + + /** {@inheritDoc} */ + @Override + public boolean isCategory2() { + return false; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return type.toHuman(); + } + + /** + * Gets the underlying type (as opposed to the type corresponding + * to this instance as a constant, which is always + * {@code Class}). + * + * @return {@code non-null;} the type corresponding to the name + */ + public Type getClassType() { + return type; + } + + /** + * Gets the type descriptor for this instance. + * + * @return {@code non-null;} the descriptor + */ + public CstString getDescriptor() { + if (descriptor == null) { + descriptor = new CstString(type.getDescriptor()); } - /** - * Returns a human readable package name for this type, like "java.util". - * If this is an array type, this returns the package name of the array's - * component type. If this is a primitive type, this returns "default". - */ - public String getPackageName() { - // descriptor is a string like "[[Ljava/util/String;" - String descriptor = getDescriptor().getString(); - int lastSlash = descriptor.lastIndexOf('/'); - int lastLeftSquare = descriptor.lastIndexOf('['); // -1 unless this is an array - if (lastSlash == -1) { - return "default"; - } else { - // +2 to skip the '[' and the 'L' prefix - return descriptor.substring(lastLeftSquare + 2, lastSlash).replace('/', '.'); - } + return descriptor; + } + + /** + * Returns a human readable package name for this type, like "java.util". + * If this is an array type, this returns the package name of the array's + * component type. If this is a primitive type, this returns "default". + */ + public String getPackageName() { + // descriptor is a string like "[[Ljava/util/String;" + String descriptor = getDescriptor().getString(); + int lastSlash = descriptor.lastIndexOf('/'); + int lastLeftSquare = descriptor.lastIndexOf('['); // -1 unless this is an array + if (lastSlash == -1) { + return "default"; + } else { + // +2 to skip the '[' and the 'L' prefix + return descriptor.substring(lastLeftSquare + 2, lastSlash).replace('/', '.'); } + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/StdConstantPool.java b/dx/src/com/android/jack/dx/rop/cst/StdConstantPool.java index ab16a2e..3036484 100644 --- a/dx/src/com/android/jack/dx/rop/cst/StdConstantPool.java +++ b/dx/src/com/android/jack/dx/rop/cst/StdConstantPool.java @@ -24,116 +24,117 @@ import com.android.jack.dx.util.MutabilityControl; * Standard implementation of {@link ConstantPool}, which directly stores * an array of {@link Constant} objects and can be made immutable. */ -public final class StdConstantPool - extends MutabilityControl implements ConstantPool { - /** {@code non-null;} array of entries */ - private final Constant[] entries; - - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size the size of the pool; this corresponds to the - * class file field {@code constant_pool_count}, and is in fact - * always at least one more than the actual size of the constant pool, - * as element {@code 0} is always invalid. - */ - public StdConstantPool(int size) { - super(size > 1); - - if (size < 1) { - throw new IllegalArgumentException("size < 1"); - } - - entries = new Constant[size]; +public final class StdConstantPool extends MutabilityControl implements ConstantPool { + /** {@code non-null;} array of entries */ + private final Constant[] entries; + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size the size of the pool; this corresponds to the + * class file field {@code constant_pool_count}, and is in fact + * always at least one more than the actual size of the constant pool, + * as element {@code 0} is always invalid. + */ + public StdConstantPool(int size) { + super(size > 1); + + if (size < 1) { + throw new IllegalArgumentException("size < 1"); } - /** {@inheritDoc} */ - public int size() { - return entries.length; + entries = new Constant[size]; + } + + /** {@inheritDoc} */ + @Override + public int size() { + return entries.length; + } + + /** {@inheritDoc} */ + @Override + public Constant getOrNull(int n) { + try { + return entries[n]; + } catch (IndexOutOfBoundsException ex) { + // Translate the exception. + return throwInvalid(n); } + } - /** {@inheritDoc} */ - public Constant getOrNull(int n) { - try { - return entries[n]; - } catch (IndexOutOfBoundsException ex) { - // Translate the exception. - return throwInvalid(n); - } + /** {@inheritDoc} */ + @Override + public Constant get0Ok(int n) { + if (n == 0) { + return null; } - /** {@inheritDoc} */ - public Constant get0Ok(int n) { - if (n == 0) { - return null; - } + return get(n); + } - return get(n); + /** {@inheritDoc} */ + @Override + public Constant get(int n) { + try { + Constant result = entries[n]; + + if (result == null) { + throwInvalid(n); + } + + return result; + } catch (IndexOutOfBoundsException ex) { + // Translate the exception. + return throwInvalid(n); } + } - /** {@inheritDoc} */ - public Constant get(int n) { - try { - Constant result = entries[n]; + /** + * Sets the entry at the given index. + * + * @param n {@code >= 1, < size();} which entry + * @param cst {@code null-ok;} the constant to store + */ + public void set(int n, Constant cst) { + throwIfImmutable(); - if (result == null) { - throwInvalid(n); - } + boolean cat2 = (cst != null) && cst.isCategory2(); - return result; - } catch (IndexOutOfBoundsException ex) { - // Translate the exception. - return throwInvalid(n); - } + if (n < 1) { + throw new IllegalArgumentException("n < 1"); } - /** - * Sets the entry at the given index. - * - * @param n {@code >= 1, < size();} which entry - * @param cst {@code null-ok;} the constant to store - */ - public void set(int n, Constant cst) { - throwIfImmutable(); - - boolean cat2 = (cst != null) && cst.isCategory2(); - - if (n < 1) { - throw new IllegalArgumentException("n < 1"); - } - - if (cat2) { - // Storing a category-2 entry nulls out the next index. - if (n == (entries.length - 1)) { - throw new IllegalArgumentException("(n == size - 1) && " + - "cst.isCategory2()"); - } - entries[n + 1] = null; - } - - if ((cst != null) && (entries[n] == null)) { - /* - * Overwriting the second half of a category-2 entry nulls out - * the first half. - */ - Constant prev = entries[n - 1]; - if ((prev != null) && prev.isCategory2()) { - entries[n - 1] = null; - } - } - - entries[n] = cst; + if (cat2) { + // Storing a category-2 entry nulls out the next index. + if (n == (entries.length - 1)) { + throw new IllegalArgumentException("(n == size - 1) && " + "cst.isCategory2()"); + } + entries[n + 1] = null; } - /** - * Throws the right exception for an invalid cpi. - * - * @param idx the bad cpi - * @return never - * @throws ExceptionWithContext always thrown - */ - private static Constant throwInvalid(int idx) { - throw new ExceptionWithContext("invalid constant pool index " + - Hex.u2(idx)); + if ((cst != null) && (entries[n] == null)) { + /* + * Overwriting the second half of a category-2 entry nulls out + * the first half. + */ + Constant prev = entries[n - 1]; + if ((prev != null) && prev.isCategory2()) { + entries[n - 1] = null; + } } + + entries[n] = cst; + } + + /** + * Throws the right exception for an invalid cpi. + * + * @param idx the bad cpi + * @return never + * @throws ExceptionWithContext always thrown + */ + private static Constant throwInvalid(int idx) { + throw new ExceptionWithContext("invalid constant pool index " + Hex.u2(idx)); + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/TypedConstant.java b/dx/src/com/android/jack/dx/rop/cst/TypedConstant.java index b7b236e..2e44b28 100644 --- a/dx/src/com/android/jack/dx/rop/cst/TypedConstant.java +++ b/dx/src/com/android/jack/dx/rop/cst/TypedConstant.java @@ -21,29 +21,32 @@ import com.android.jack.dx.rop.type.TypeBearer; /** * Base class for constants which implement {@link TypeBearer}. */ -public abstract class TypedConstant - extends Constant implements TypeBearer { - /** - * {@inheritDoc} - * - * This implementation always returns {@code this}. - */ - public final TypeBearer getFrameType() { - return this; - } +public abstract class TypedConstant extends Constant implements TypeBearer { + /** + * {@inheritDoc} + * + * This implementation always returns {@code this}. + */ + @Override + public final TypeBearer getFrameType() { + return this; + } - /** {@inheritDoc} */ - public final int getBasicType() { - return getType().getBasicType(); - } + /** {@inheritDoc} */ + @Override + public final int getBasicType() { + return getType().getBasicType(); + } - /** {@inheritDoc} */ - public final int getBasicFrameType() { - return getType().getBasicFrameType(); - } + /** {@inheritDoc} */ + @Override + public final int getBasicFrameType() { + return getType().getBasicFrameType(); + } - /** {@inheritDoc} */ - public final boolean isConstant() { - return true; - } + /** {@inheritDoc} */ + @Override + public final boolean isConstant() { + return true; + } } diff --git a/dx/src/com/android/jack/dx/rop/cst/Zeroes.java b/dx/src/com/android/jack/dx/rop/cst/Zeroes.java index 00842cb..2550ec0 100644 --- a/dx/src/com/android/jack/dx/rop/cst/Zeroes.java +++ b/dx/src/com/android/jack/dx/rop/cst/Zeroes.java @@ -22,34 +22,42 @@ import com.android.jack.dx.rop.type.Type; * Utility for turning types into zeroes. */ public final class Zeroes { - /** - * This class is uninstantiable. - */ - private Zeroes() { - // This space intentionally left blank. - } + /** + * This class is uninstantiable. + */ + private Zeroes() { + // This space intentionally left blank. + } - /** - * Gets the "zero" (or {@code null}) value for the given type. - * - * @param type {@code non-null;} the type in question - * @return {@code non-null;} its "zero" value - */ - public static Constant zeroFor(Type type) { - switch (type.getBasicType()) { - case Type.BT_BOOLEAN: return CstBoolean.VALUE_FALSE; - case Type.BT_BYTE: return CstByte.VALUE_0; - case Type.BT_CHAR: return CstChar.VALUE_0; - case Type.BT_DOUBLE: return CstDouble.VALUE_0; - case Type.BT_FLOAT: return CstFloat.VALUE_0; - case Type.BT_INT: return CstInteger.VALUE_0; - case Type.BT_LONG: return CstLong.VALUE_0; - case Type.BT_SHORT: return CstShort.VALUE_0; - case Type.BT_OBJECT: return CstKnownNull.THE_ONE; - default: { - throw new UnsupportedOperationException("no zero for type: " + - type.toHuman()); - } - } + /** + * Gets the "zero" (or {@code null}) value for the given type. + * + * @param type {@code non-null;} the type in question + * @return {@code non-null;} its "zero" value + */ + public static Constant zeroFor(Type type) { + switch (type.getBasicType()) { + case Type.BT_BOOLEAN: + return CstBoolean.VALUE_FALSE; + case Type.BT_BYTE: + return CstByte.VALUE_0; + case Type.BT_CHAR: + return CstChar.VALUE_0; + case Type.BT_DOUBLE: + return CstDouble.VALUE_0; + case Type.BT_FLOAT: + return CstFloat.VALUE_0; + case Type.BT_INT: + return CstInteger.VALUE_0; + case Type.BT_LONG: + return CstLong.VALUE_0; + case Type.BT_SHORT: + return CstShort.VALUE_0; + case Type.BT_OBJECT: + return CstKnownNull.THE_ONE; + default: { + throw new UnsupportedOperationException("no zero for type: " + type.toHuman()); + } } + } } diff --git a/dx/src/com/android/jack/dx/rop/type/Prototype.java b/dx/src/com/android/jack/dx/rop/type/Prototype.java index 547f1ca..ed4e46c 100644 --- a/dx/src/com/android/jack/dx/rop/type/Prototype.java +++ b/dx/src/com/android/jack/dx/rop/type/Prototype.java @@ -24,377 +24,374 @@ import java.util.HashMap; * using {@code ==}. */ public final class Prototype implements Comparable<Prototype> { - /** {@code non-null;} intern table mapping string descriptors to instances */ - private static final HashMap<String, Prototype> internTable = - new HashMap<String, Prototype>(500); - - /** {@code non-null;} method descriptor */ - private final String descriptor; - - /** {@code non-null;} return type */ - private final Type returnType; - - /** {@code non-null;} list of parameter types */ - private final StdTypeList parameterTypes; - - /** {@code null-ok;} list of parameter frame types, if calculated */ - private StdTypeList parameterFrameTypes; - - /** - * Returns the unique instance corresponding to the - * given method descriptor. See vmspec-2 sec4.3.3 for details on the - * field descriptor syntax. - * - * @param descriptor {@code non-null;} the descriptor - * @return {@code non-null;} the corresponding instance - * @throws IllegalArgumentException thrown if the descriptor has - * invalid syntax - */ - public static Prototype intern(String descriptor) { - if (descriptor == null) { - throw new NullPointerException("descriptor == null"); - } - - Prototype result; - synchronized (internTable) { - result = internTable.get(descriptor); - } - if (result != null) { - return result; - } - - Type[] params = makeParameterArray(descriptor); - int paramCount = 0; - int at = 1; - - for (;;) { - int startAt = at; - char c = descriptor.charAt(at); - if (c == ')') { - at++; - break; - } - - // Skip array markers. - while (c == '[') { - at++; - c = descriptor.charAt(at); - } - - if (c == 'L') { - // It looks like the start of a class name; find the end. - int endAt = descriptor.indexOf(';', at); - if (endAt == -1) { - throw new IllegalArgumentException("bad descriptor"); - } - at = endAt + 1; - } else { - at++; - } - - params[paramCount] = - Type.intern(descriptor.substring(startAt, at)); - paramCount++; - } - - Type returnType = Type.internReturnType(descriptor.substring(at)); - StdTypeList parameterTypes = new StdTypeList(paramCount); - - for (int i = 0; i < paramCount; i++) { - parameterTypes.set(i, params[i]); - } - - result = new Prototype(descriptor, returnType, parameterTypes); - return putIntern(result); + /** {@code non-null;} intern table mapping string descriptors to instances */ + private static final HashMap<String, Prototype> internTable = new HashMap<String, Prototype>(500); + + /** {@code non-null;} method descriptor */ + private final String descriptor; + + /** {@code non-null;} return type */ + private final Type returnType; + + /** {@code non-null;} list of parameter types */ + private final StdTypeList parameterTypes; + + /** {@code null-ok;} list of parameter frame types, if calculated */ + private StdTypeList parameterFrameTypes; + + /** + * Returns the unique instance corresponding to the + * given method descriptor. See vmspec-2 sec4.3.3 for details on the + * field descriptor syntax. + * + * @param descriptor {@code non-null;} the descriptor + * @return {@code non-null;} the corresponding instance + * @throws IllegalArgumentException thrown if the descriptor has + * invalid syntax + */ + public static Prototype intern(String descriptor) { + if (descriptor == null) { + throw new NullPointerException("descriptor == null"); } - /** - * Helper for {@link #intern} which returns an empty array to - * populate with parsed parameter types, and which also ensures - * that there is a '(' at the start of the descriptor and a - * single ')' somewhere before the end. - * - * @param descriptor {@code non-null;} the descriptor string - * @return {@code non-null;} array large enough to hold all parsed parameter - * types, but which is likely actually larger than needed - */ - private static Type[] makeParameterArray(String descriptor) { - int length = descriptor.length(); - - if (descriptor.charAt(0) != '(') { - throw new IllegalArgumentException("bad descriptor"); - } - - /* - * This is a cheesy way to establish an upper bound on the - * number of parameters: Just count capital letters. - */ - int closeAt = 0; - int maxParams = 0; - for (int i = 1; i < length; i++) { - char c = descriptor.charAt(i); - if (c == ')') { - closeAt = i; - break; - } - if ((c >= 'A') && (c <= 'Z')) { - maxParams++; - } - } - - if ((closeAt == 0) || (closeAt == (length - 1))) { - throw new IllegalArgumentException("bad descriptor"); - } - - if (descriptor.indexOf(')', closeAt + 1) != -1) { - throw new IllegalArgumentException("bad descriptor"); - } - - return new Type[maxParams]; + Prototype result; + synchronized (internTable) { + result = internTable.get(descriptor); + } + if (result != null) { + return result; } - /** - * Interns an instance, adding to the descriptor as necessary based - * on the given definer, name, and flags. For example, an init - * method has an uninitialized object of type {@code definer} - * as its first argument. - * - * @param descriptor {@code non-null;} the descriptor string - * @param definer {@code non-null;} class the method is defined on - * @param isStatic whether this is a static method - * @param isInit whether this is an init method - * @return {@code non-null;} the interned instance - */ - public static Prototype intern(String descriptor, Type definer, - boolean isStatic, boolean isInit) { - Prototype base = intern(descriptor); - - if (isStatic) { - return base; - } - - if (isInit) { - definer = definer.asUninitialized(Integer.MAX_VALUE); + Type[] params = makeParameterArray(descriptor); + int paramCount = 0; + int at = 1; + + for (;;) { + int startAt = at; + char c = descriptor.charAt(at); + if (c == ')') { + at++; + break; + } + + // Skip array markers. + while (c == '[') { + at++; + c = descriptor.charAt(at); + } + + if (c == 'L') { + // It looks like the start of a class name; find the end. + int endAt = descriptor.indexOf(';', at); + if (endAt == -1) { + throw new IllegalArgumentException("bad descriptor"); } + at = endAt + 1; + } else { + at++; + } - return base.withFirstParameter(definer); + params[paramCount] = Type.intern(descriptor.substring(startAt, at)); + paramCount++; } - /** - * Interns an instance which consists of the given number of - * {@code int}s along with the given return type - * - * @param returnType {@code non-null;} the return type - * @param count {@code > 0;} the number of elements in the prototype - * @return {@code non-null;} the interned instance - */ - public static Prototype internInts(Type returnType, int count) { - // Make the descriptor... - - StringBuffer sb = new StringBuffer(100); - - sb.append('('); - - for (int i = 0; i < count; i++) { - sb.append('I'); - } + Type returnType = Type.internReturnType(descriptor.substring(at)); + StdTypeList parameterTypes = new StdTypeList(paramCount); - sb.append(')'); - sb.append(returnType.getDescriptor()); + for (int i = 0; i < paramCount; i++) { + parameterTypes.set(i, params[i]); + } - // ...and intern it. - return intern(sb.toString()); + result = new Prototype(descriptor, returnType, parameterTypes); + return putIntern(result); + } + + /** + * Helper for {@link #intern} which returns an empty array to + * populate with parsed parameter types, and which also ensures + * that there is a '(' at the start of the descriptor and a + * single ')' somewhere before the end. + * + * @param descriptor {@code non-null;} the descriptor string + * @return {@code non-null;} array large enough to hold all parsed parameter + * types, but which is likely actually larger than needed + */ + private static Type[] makeParameterArray(String descriptor) { + int length = descriptor.length(); + + if (descriptor.charAt(0) != '(') { + throw new IllegalArgumentException("bad descriptor"); } - /** - * Constructs an instance. This is a private constructor; use one - * of the public static methods to get instances. - * - * @param descriptor {@code non-null;} the descriptor string + /* + * This is a cheesy way to establish an upper bound on the + * number of parameters: Just count capital letters. */ - private Prototype(String descriptor, Type returnType, - StdTypeList parameterTypes) { - if (descriptor == null) { - throw new NullPointerException("descriptor == null"); - } - - if (returnType == null) { - throw new NullPointerException("returnType == null"); - } - - if (parameterTypes == null) { - throw new NullPointerException("parameterTypes == null"); - } - - this.descriptor = descriptor; - this.returnType = returnType; - this.parameterTypes = parameterTypes; - this.parameterFrameTypes = null; + int closeAt = 0; + int maxParams = 0; + for (int i = 1; i < length; i++) { + char c = descriptor.charAt(i); + if (c == ')') { + closeAt = i; + break; + } + if ((c >= 'A') && (c <= 'Z')) { + maxParams++; + } } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (this == other) { - /* - * Since externally-visible instances are interned, this - * check helps weed out some easy cases. - */ - return true; - } - - if (!(other instanceof Prototype)) { - return false; - } - - return descriptor.equals(((Prototype) other).descriptor); + if ((closeAt == 0) || (closeAt == (length - 1))) { + throw new IllegalArgumentException("bad descriptor"); } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return descriptor.hashCode(); + if (descriptor.indexOf(')', closeAt + 1) != -1) { + throw new IllegalArgumentException("bad descriptor"); } - /** {@inheritDoc} */ - public int compareTo(Prototype other) { - if (this == other) { - return 0; - } + return new Type[maxParams]; + } + + /** + * Interns an instance, adding to the descriptor as necessary based + * on the given definer, name, and flags. For example, an init + * method has an uninitialized object of type {@code definer} + * as its first argument. + * + * @param descriptor {@code non-null;} the descriptor string + * @param definer {@code non-null;} class the method is defined on + * @param isStatic whether this is a static method + * @param isInit whether this is an init method + * @return {@code non-null;} the interned instance + */ + public static Prototype intern(String descriptor, Type definer, boolean isStatic, + boolean isInit) { + Prototype base = intern(descriptor); + + if (isStatic) { + return base; + } - /* - * The return type is the major order, and then args in order, - * and then the shorter list comes first (similar to string - * sorting). - */ + if (isInit) { + definer = definer.asUninitialized(Integer.MAX_VALUE); + } - int result = returnType.compareTo(other.returnType); + return base.withFirstParameter(definer); + } - if (result != 0) { - return result; - } + /** + * Interns an instance which consists of the given number of + * {@code int}s along with the given return type + * + * @param returnType {@code non-null;} the return type + * @param count {@code > 0;} the number of elements in the prototype + * @return {@code non-null;} the interned instance + */ + public static Prototype internInts(Type returnType, int count) { + // Make the descriptor... - int thisSize = parameterTypes.size(); - int otherSize = other.parameterTypes.size(); - int size = Math.min(thisSize, otherSize); + StringBuffer sb = new StringBuffer(100); - for (int i = 0; i < size; i++) { - Type thisType = parameterTypes.get(i); - Type otherType = other.parameterTypes.get(i); + sb.append('('); - result = thisType.compareTo(otherType); + for (int i = 0; i < count; i++) { + sb.append('I'); + } - if (result != 0) { - return result; - } - } + sb.append(')'); + sb.append(returnType.getDescriptor()); + + // ...and intern it. + return intern(sb.toString()); + } + + /** + * Constructs an instance. This is a private constructor; use one + * of the public static methods to get instances. + * + * @param descriptor {@code non-null;} the descriptor string + */ + private Prototype(String descriptor, Type returnType, StdTypeList parameterTypes) { + if (descriptor == null) { + throw new NullPointerException("descriptor == null"); + } - if (thisSize < otherSize) { - return -1; - } else if (thisSize > otherSize) { - return 1; - } else { - return 0; - } + if (returnType == null) { + throw new NullPointerException("returnType == null"); } - /** {@inheritDoc} */ - @Override - public String toString() { - return descriptor; + if (parameterTypes == null) { + throw new NullPointerException("parameterTypes == null"); } - /** - * Gets the descriptor string. - * - * @return {@code non-null;} the descriptor - */ - public String getDescriptor() { - return descriptor; + this.descriptor = descriptor; + this.returnType = returnType; + this.parameterTypes = parameterTypes; + this.parameterFrameTypes = null; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (this == other) { + /* + * Since externally-visible instances are interned, this + * check helps weed out some easy cases. + */ + return true; } - /** - * Gets the return type. - * - * @return {@code non-null;} the return type - */ - public Type getReturnType() { - return returnType; + if (!(other instanceof Prototype)) { + return false; } - /** - * Gets the list of parameter types. - * - * @return {@code non-null;} the list of parameter types - */ - public StdTypeList getParameterTypes() { - return parameterTypes; + return descriptor.equals(((Prototype) other).descriptor); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return descriptor.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(Prototype other) { + if (this == other) { + return 0; } - /** - * Gets the list of frame types corresponding to the list of parameter - * types. The difference between the two lists (if any) is that all - * "intlike" types (see {@link Type#isIntlike}) are replaced by - * {@link Type#INT}. - * - * @return {@code non-null;} the list of parameter frame types + /* + * The return type is the major order, and then args in order, + * and then the shorter list comes first (similar to string + * sorting). */ - public StdTypeList getParameterFrameTypes() { - if (parameterFrameTypes == null) { - int sz = parameterTypes.size(); - StdTypeList list = new StdTypeList(sz); - boolean any = false; - for (int i = 0; i < sz; i++) { - Type one = parameterTypes.get(i); - if (one.isIntlike()) { - any = true; - one = Type.INT; - } - list.set(i, one); - } - parameterFrameTypes = any ? list : parameterTypes; - } - return parameterFrameTypes; +int result = returnType.compareTo(other.returnType); + + if (result != 0) { + return result; } - /** - * Returns a new interned instance, which is the same as this instance, - * except that it has an additional parameter prepended to the original's - * argument list. - * - * @param param {@code non-null;} the new first parameter - * @return {@code non-null;} an appropriately-constructed instance - */ - public Prototype withFirstParameter(Type param) { - String newDesc = "(" + param.getDescriptor() + descriptor.substring(1); - StdTypeList newParams = parameterTypes.withFirst(param); + int thisSize = parameterTypes.size(); + int otherSize = other.parameterTypes.size(); + int size = Math.min(thisSize, otherSize); - newParams.setImmutable(); + for (int i = 0; i < size; i++) { + Type thisType = parameterTypes.get(i); + Type otherType = other.parameterTypes.get(i); - Prototype result = - new Prototype(newDesc, returnType, newParams); + result = thisType.compareTo(otherType); - return putIntern(result); + if (result != 0) { + return result; + } } - /** - * Puts the given instance in the intern table if it's not already - * there. If a conflicting value is already in the table, then leave it. - * Return the interned value. - * - * @param desc {@code non-null;} instance to make interned - * @return {@code non-null;} the actual interned object - */ - private static Prototype putIntern(Prototype desc) { - synchronized (internTable) { - String descriptor = desc.getDescriptor(); - Prototype already = internTable.get(descriptor); - if (already != null) { - return already; - } - internTable.put(descriptor, desc); - return desc; + if (thisSize < otherSize) { + return -1; + } else if (thisSize > otherSize) { + return 1; + } else { + return 0; + } + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return descriptor; + } + + /** + * Gets the descriptor string. + * + * @return {@code non-null;} the descriptor + */ + public String getDescriptor() { + return descriptor; + } + + /** + * Gets the return type. + * + * @return {@code non-null;} the return type + */ + public Type getReturnType() { + return returnType; + } + + /** + * Gets the list of parameter types. + * + * @return {@code non-null;} the list of parameter types + */ + public StdTypeList getParameterTypes() { + return parameterTypes; + } + + /** + * Gets the list of frame types corresponding to the list of parameter + * types. The difference between the two lists (if any) is that all + * "intlike" types (see {@link Type#isIntlike}) are replaced by + * {@link Type#INT}. + * + * @return {@code non-null;} the list of parameter frame types + */ + public StdTypeList getParameterFrameTypes() { + if (parameterFrameTypes == null) { + int sz = parameterTypes.size(); + StdTypeList list = new StdTypeList(sz); + boolean any = false; + for (int i = 0; i < sz; i++) { + Type one = parameterTypes.get(i); + if (one.isIntlike()) { + any = true; + one = Type.INT; } + list.set(i, one); + } + parameterFrameTypes = any ? list : parameterTypes; + } + + return parameterFrameTypes; + } + + /** + * Returns a new interned instance, which is the same as this instance, + * except that it has an additional parameter prepended to the original's + * argument list. + * + * @param param {@code non-null;} the new first parameter + * @return {@code non-null;} an appropriately-constructed instance + */ + public Prototype withFirstParameter(Type param) { + String newDesc = "(" + param.getDescriptor() + descriptor.substring(1); + StdTypeList newParams = parameterTypes.withFirst(param); + + newParams.setImmutable(); + + Prototype result = new Prototype(newDesc, returnType, newParams); + + return putIntern(result); + } + + /** + * Puts the given instance in the intern table if it's not already + * there. If a conflicting value is already in the table, then leave it. + * Return the interned value. + * + * @param desc {@code non-null;} instance to make interned + * @return {@code non-null;} the actual interned object + */ + private static Prototype putIntern(Prototype desc) { + synchronized (internTable) { + String descriptor = desc.getDescriptor(); + Prototype already = internTable.get(descriptor); + if (already != null) { + return already; + } + internTable.put(descriptor, desc); + return desc; } + } } diff --git a/dx/src/com/android/jack/dx/rop/type/StdTypeList.java b/dx/src/com/android/jack/dx/rop/type/StdTypeList.java index fe8242e..c4b5c4a 100644 --- a/dx/src/com/android/jack/dx/rop/type/StdTypeList.java +++ b/dx/src/com/android/jack/dx/rop/type/StdTypeList.java @@ -21,387 +21,367 @@ import com.android.jack.dx.util.FixedSizeList; /** * Standard implementation of {@link TypeList}. */ -public final class StdTypeList - extends FixedSizeList implements TypeList { - /** {@code non-null;} no-element instance */ - public static final StdTypeList EMPTY = new StdTypeList(0); +public final class StdTypeList extends FixedSizeList implements TypeList { + /** {@code non-null;} no-element instance */ + public static final StdTypeList EMPTY = new StdTypeList(0); - /** {@code non-null;} the list {@code [int]} */ - public static final StdTypeList INT = StdTypeList.make(Type.INT); + /** {@code non-null;} the list {@code [int]} */ + public static final StdTypeList INT = StdTypeList.make(Type.INT); - /** {@code non-null;} the list {@code [long]} */ - public static final StdTypeList LONG = StdTypeList.make(Type.LONG); + /** {@code non-null;} the list {@code [long]} */ + public static final StdTypeList LONG = StdTypeList.make(Type.LONG); - /** {@code non-null;} the list {@code [float]} */ - public static final StdTypeList FLOAT = StdTypeList.make(Type.FLOAT); + /** {@code non-null;} the list {@code [float]} */ + public static final StdTypeList FLOAT = StdTypeList.make(Type.FLOAT); - /** {@code non-null;} the list {@code [double]} */ - public static final StdTypeList DOUBLE = StdTypeList.make(Type.DOUBLE); + /** {@code non-null;} the list {@code [double]} */ + public static final StdTypeList DOUBLE = StdTypeList.make(Type.DOUBLE); - /** {@code non-null;} the list {@code [Object]} */ - public static final StdTypeList OBJECT = StdTypeList.make(Type.OBJECT); + /** {@code non-null;} the list {@code [Object]} */ + public static final StdTypeList OBJECT = StdTypeList.make(Type.OBJECT); - /** {@code non-null;} the list {@code [ReturnAddress]} */ - public static final StdTypeList RETURN_ADDRESS - = StdTypeList.make(Type.RETURN_ADDRESS); + /** {@code non-null;} the list {@code [ReturnAddress]} */ + public static final StdTypeList RETURN_ADDRESS = StdTypeList.make(Type.RETURN_ADDRESS); - /** {@code non-null;} the list {@code [Throwable]} */ - public static final StdTypeList THROWABLE = - StdTypeList.make(Type.THROWABLE); + /** {@code non-null;} the list {@code [Throwable]} */ + public static final StdTypeList THROWABLE = StdTypeList.make(Type.THROWABLE); - /** {@code non-null;} the list {@code [int, int]} */ - public static final StdTypeList INT_INT = - StdTypeList.make(Type.INT, Type.INT); + /** {@code non-null;} the list {@code [int, int]} */ + public static final StdTypeList INT_INT = StdTypeList.make(Type.INT, Type.INT); - /** {@code non-null;} the list {@code [long, long]} */ - public static final StdTypeList LONG_LONG = - StdTypeList.make(Type.LONG, Type.LONG); + /** {@code non-null;} the list {@code [long, long]} */ + public static final StdTypeList LONG_LONG = StdTypeList.make(Type.LONG, Type.LONG); - /** {@code non-null;} the list {@code [float, float]} */ - public static final StdTypeList FLOAT_FLOAT = - StdTypeList.make(Type.FLOAT, Type.FLOAT); + /** {@code non-null;} the list {@code [float, float]} */ + public static final StdTypeList FLOAT_FLOAT = StdTypeList.make(Type.FLOAT, Type.FLOAT); - /** {@code non-null;} the list {@code [double, double]} */ - public static final StdTypeList DOUBLE_DOUBLE = - StdTypeList.make(Type.DOUBLE, Type.DOUBLE); + /** {@code non-null;} the list {@code [double, double]} */ + public static final StdTypeList DOUBLE_DOUBLE = StdTypeList.make(Type.DOUBLE, Type.DOUBLE); - /** {@code non-null;} the list {@code [Object, Object]} */ - public static final StdTypeList OBJECT_OBJECT = - StdTypeList.make(Type.OBJECT, Type.OBJECT); - - /** {@code non-null;} the list {@code [int, Object]} */ - public static final StdTypeList INT_OBJECT = - StdTypeList.make(Type.INT, Type.OBJECT); - - /** {@code non-null;} the list {@code [long, Object]} */ - public static final StdTypeList LONG_OBJECT = - StdTypeList.make(Type.LONG, Type.OBJECT); - - /** {@code non-null;} the list {@code [float, Object]} */ - public static final StdTypeList FLOAT_OBJECT = - StdTypeList.make(Type.FLOAT, Type.OBJECT); - - /** {@code non-null;} the list {@code [double, Object]} */ - public static final StdTypeList DOUBLE_OBJECT = - StdTypeList.make(Type.DOUBLE, Type.OBJECT); - - /** {@code non-null;} the list {@code [long, int]} */ - public static final StdTypeList LONG_INT = - StdTypeList.make(Type.LONG, Type.INT); - - /** {@code non-null;} the list {@code [int[], int]} */ - public static final StdTypeList INTARR_INT = - StdTypeList.make(Type.INT_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [long[], int]} */ - public static final StdTypeList LONGARR_INT = - StdTypeList.make(Type.LONG_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [float[], int]} */ - public static final StdTypeList FLOATARR_INT = - StdTypeList.make(Type.FLOAT_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [double[], int]} */ - public static final StdTypeList DOUBLEARR_INT = - StdTypeList.make(Type.DOUBLE_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [Object[], int]} */ - public static final StdTypeList OBJECTARR_INT = - StdTypeList.make(Type.OBJECT_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [boolean[], int]} */ - public static final StdTypeList BOOLEANARR_INT = - StdTypeList.make(Type.BOOLEAN_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [byte[], int]} */ - public static final StdTypeList BYTEARR_INT = - StdTypeList.make(Type.BYTE_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [char[], int]} */ - public static final StdTypeList CHARARR_INT = - StdTypeList.make(Type.CHAR_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [short[], int]} */ - public static final StdTypeList SHORTARR_INT = - StdTypeList.make(Type.SHORT_ARRAY, Type.INT); + /** {@code non-null;} the list {@code [Object, Object]} */ + public static final StdTypeList OBJECT_OBJECT = StdTypeList.make(Type.OBJECT, Type.OBJECT); - /** {@code non-null;} the list {@code [int, int[], int]} */ - public static final StdTypeList INT_INTARR_INT = - StdTypeList.make(Type.INT, Type.INT_ARRAY, Type.INT); + /** {@code non-null;} the list {@code [int, Object]} */ + public static final StdTypeList INT_OBJECT = StdTypeList.make(Type.INT, Type.OBJECT); - /** {@code non-null;} the list {@code [long, long[], int]} */ - public static final StdTypeList LONG_LONGARR_INT = - StdTypeList.make(Type.LONG, Type.LONG_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [float, float[], int]} */ - public static final StdTypeList FLOAT_FLOATARR_INT = - StdTypeList.make(Type.FLOAT, Type.FLOAT_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [double, double[], int]} */ - public static final StdTypeList DOUBLE_DOUBLEARR_INT = - StdTypeList.make(Type.DOUBLE, Type.DOUBLE_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [Object, Object[], int]} */ - public static final StdTypeList OBJECT_OBJECTARR_INT = - StdTypeList.make(Type.OBJECT, Type.OBJECT_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [int, boolean[], int]} */ - public static final StdTypeList INT_BOOLEANARR_INT = - StdTypeList.make(Type.INT, Type.BOOLEAN_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [int, byte[], int]} */ - public static final StdTypeList INT_BYTEARR_INT = - StdTypeList.make(Type.INT, Type.BYTE_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [int, char[], int]} */ - public static final StdTypeList INT_CHARARR_INT = - StdTypeList.make(Type.INT, Type.CHAR_ARRAY, Type.INT); - - /** {@code non-null;} the list {@code [int, short[], int]} */ - public static final StdTypeList INT_SHORTARR_INT = - StdTypeList.make(Type.INT, Type.SHORT_ARRAY, Type.INT); - - /** - * Makes a single-element instance. - * - * @param type {@code non-null;} the element - * @return {@code non-null;} an appropriately-constructed instance - */ - public static StdTypeList make(Type type) { - StdTypeList result = new StdTypeList(1); - result.set(0, type); - return result; - } + /** {@code non-null;} the list {@code [long, Object]} */ + public static final StdTypeList LONG_OBJECT = StdTypeList.make(Type.LONG, Type.OBJECT); - /** - * Makes a two-element instance. - * - * @param type0 {@code non-null;} the first element - * @param type1 {@code non-null;} the second element - * @return {@code non-null;} an appropriately-constructed instance - */ - public static StdTypeList make(Type type0, Type type1) { - StdTypeList result = new StdTypeList(2); - result.set(0, type0); - result.set(1, type1); - return result; - } + /** {@code non-null;} the list {@code [float, Object]} */ + public static final StdTypeList FLOAT_OBJECT = StdTypeList.make(Type.FLOAT, Type.OBJECT); - /** - * Makes a three-element instance. - * - * @param type0 {@code non-null;} the first element - * @param type1 {@code non-null;} the second element - * @param type2 {@code non-null;} the third element - * @return {@code non-null;} an appropriately-constructed instance - */ - public static StdTypeList make(Type type0, Type type1, Type type2) { - StdTypeList result = new StdTypeList(3); - result.set(0, type0); - result.set(1, type1); - result.set(2, type2); - return result; - } + /** {@code non-null;} the list {@code [double, Object]} */ + public static final StdTypeList DOUBLE_OBJECT = StdTypeList.make(Type.DOUBLE, Type.OBJECT); - /** - * Makes a four-element instance. - * - * @param type0 {@code non-null;} the first element - * @param type1 {@code non-null;} the second element - * @param type2 {@code non-null;} the third element - * @param type3 {@code non-null;} the fourth element - * @return {@code non-null;} an appropriately-constructed instance - */ - public static StdTypeList make(Type type0, Type type1, Type type2, - Type type3) { - StdTypeList result = new StdTypeList(4); - result.set(0, type0); - result.set(1, type1); - result.set(2, type2); - result.set(3, type3); - return result; - } + /** {@code non-null;} the list {@code [long, int]} */ + public static final StdTypeList LONG_INT = StdTypeList.make(Type.LONG, Type.INT); - /** - * Returns the given list as a comma-separated list of human forms. This - * is a static method so as to work on arbitrary {@link TypeList} - * instances. - * - * @param list {@code non-null;} the list to convert - * @return {@code non-null;} the human form - */ - public static String toHuman(TypeList list) { - int size = list.size(); - - if (size == 0) { - return "<empty>"; - } - - StringBuffer sb = new StringBuffer(100); - - for (int i = 0; i < size; i++) { - if (i != 0) { - sb.append(", "); - } - sb.append(list.getType(i).toHuman()); - } - - return sb.toString(); + /** {@code non-null;} the list {@code [int[], int]} */ + public static final StdTypeList INTARR_INT = StdTypeList.make(Type.INT_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [long[], int]} */ + public static final StdTypeList LONGARR_INT = StdTypeList.make(Type.LONG_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [float[], int]} */ + public static final StdTypeList FLOATARR_INT = StdTypeList.make(Type.FLOAT_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [double[], int]} */ + public static final StdTypeList DOUBLEARR_INT = StdTypeList.make(Type.DOUBLE_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [Object[], int]} */ + public static final StdTypeList OBJECTARR_INT = StdTypeList.make(Type.OBJECT_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [boolean[], int]} */ + public static final StdTypeList BOOLEANARR_INT = StdTypeList.make(Type.BOOLEAN_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [byte[], int]} */ + public static final StdTypeList BYTEARR_INT = StdTypeList.make(Type.BYTE_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [char[], int]} */ + public static final StdTypeList CHARARR_INT = StdTypeList.make(Type.CHAR_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [short[], int]} */ + public static final StdTypeList SHORTARR_INT = StdTypeList.make(Type.SHORT_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [int, int[], int]} */ + public static final StdTypeList INT_INTARR_INT = + StdTypeList.make(Type.INT, Type.INT_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [long, long[], int]} */ + public static final StdTypeList LONG_LONGARR_INT = + StdTypeList.make(Type.LONG, Type.LONG_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [float, float[], int]} */ + public static final StdTypeList FLOAT_FLOATARR_INT = + StdTypeList.make(Type.FLOAT, Type.FLOAT_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [double, double[], int]} */ + public static final StdTypeList DOUBLE_DOUBLEARR_INT = + StdTypeList.make(Type.DOUBLE, Type.DOUBLE_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [Object, Object[], int]} */ + public static final StdTypeList OBJECT_OBJECTARR_INT = + StdTypeList.make(Type.OBJECT, Type.OBJECT_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [int, boolean[], int]} */ + public static final StdTypeList INT_BOOLEANARR_INT = + StdTypeList.make(Type.INT, Type.BOOLEAN_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [int, byte[], int]} */ + public static final StdTypeList INT_BYTEARR_INT = + StdTypeList.make(Type.INT, Type.BYTE_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [int, char[], int]} */ + public static final StdTypeList INT_CHARARR_INT = + StdTypeList.make(Type.INT, Type.CHAR_ARRAY, Type.INT); + + /** {@code non-null;} the list {@code [int, short[], int]} */ + public static final StdTypeList INT_SHORTARR_INT = + StdTypeList.make(Type.INT, Type.SHORT_ARRAY, Type.INT); + + /** + * Makes a single-element instance. + * + * @param type {@code non-null;} the element + * @return {@code non-null;} an appropriately-constructed instance + */ + public static StdTypeList make(Type type) { + StdTypeList result = new StdTypeList(1); + result.set(0, type); + return result; + } + + /** + * Makes a two-element instance. + * + * @param type0 {@code non-null;} the first element + * @param type1 {@code non-null;} the second element + * @return {@code non-null;} an appropriately-constructed instance + */ + public static StdTypeList make(Type type0, Type type1) { + StdTypeList result = new StdTypeList(2); + result.set(0, type0); + result.set(1, type1); + return result; + } + + /** + * Makes a three-element instance. + * + * @param type0 {@code non-null;} the first element + * @param type1 {@code non-null;} the second element + * @param type2 {@code non-null;} the third element + * @return {@code non-null;} an appropriately-constructed instance + */ + public static StdTypeList make(Type type0, Type type1, Type type2) { + StdTypeList result = new StdTypeList(3); + result.set(0, type0); + result.set(1, type1); + result.set(2, type2); + return result; + } + + /** + * Makes a four-element instance. + * + * @param type0 {@code non-null;} the first element + * @param type1 {@code non-null;} the second element + * @param type2 {@code non-null;} the third element + * @param type3 {@code non-null;} the fourth element + * @return {@code non-null;} an appropriately-constructed instance + */ + public static StdTypeList make(Type type0, Type type1, Type type2, Type type3) { + StdTypeList result = new StdTypeList(4); + result.set(0, type0); + result.set(1, type1); + result.set(2, type2); + result.set(3, type3); + return result; + } + + /** + * Returns the given list as a comma-separated list of human forms. This + * is a static method so as to work on arbitrary {@link TypeList} + * instances. + * + * @param list {@code non-null;} the list to convert + * @return {@code non-null;} the human form + */ + public static String toHuman(TypeList list) { + int size = list.size(); + + if (size == 0) { + return "<empty>"; } - /** - * Returns a hashcode of the contents of the given list. This - * is a static method so as to work on arbitrary {@link TypeList} - * instances. - * - * @param list {@code non-null;} the list to inspect - * @return {@code non-null;} the hash code - */ - public static int hashContents(TypeList list) { - int size = list.size(); - int hash = 0; - - for (int i = 0; i < size; i++) { - hash = (hash * 31) + list.getType(i).hashCode(); - } - - return hash; - } + StringBuffer sb = new StringBuffer(100); - /** - * Compares the contents of the given two instances for equality. This - * is a static method so as to work on arbitrary {@link TypeList} - * instances. - * - * @param list1 {@code non-null;} one list to compare - * @param list2 {@code non-null;} another list to compare - * @return whether the two lists contain corresponding equal elements - */ - public static boolean equalContents(TypeList list1, TypeList list2) { - int size = list1.size(); - - if (list2.size() != size) { - return false; - } - - for (int i = 0; i < size; i++) { - if (! list1.getType(i).equals(list2.getType(i))) { - return false; - } - } - - return true; + for (int i = 0; i < size; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(list.getType(i).toHuman()); } - /** - * Compares the contents of the given two instances for ordering. This - * is a static method so as to work on arbitrary {@link TypeList} - * instances. - * - * @param list1 {@code non-null;} one list to compare - * @param list2 {@code non-null;} another list to compare - * @return the order of the two lists - */ - public static int compareContents(TypeList list1, TypeList list2) { - int size1 = list1.size(); - int size2 = list2.size(); - int size = Math.min(size1, size2); - - for (int i = 0; i < size; i++) { - int comparison = list1.getType(i).compareTo(list2.getType(i)); - if (comparison != 0) { - return comparison; - } - } - - if (size1 == size2) { - return 0; - } else if (size1 < size2) { - return -1; - } else { - return 1; - } + return sb.toString(); + } + + /** + * Returns a hashcode of the contents of the given list. This + * is a static method so as to work on arbitrary {@link TypeList} + * instances. + * + * @param list {@code non-null;} the list to inspect + * @return {@code non-null;} the hash code + */ + public static int hashContents(TypeList list) { + int size = list.size(); + int hash = 0; + + for (int i = 0; i < size; i++) { + hash = (hash * 31) + list.getType(i).hashCode(); } - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size the size of the list - */ - public StdTypeList(int size) { - super(size); + return hash; + } + + /** + * Compares the contents of the given two instances for equality. This + * is a static method so as to work on arbitrary {@link TypeList} + * instances. + * + * @param list1 {@code non-null;} one list to compare + * @param list2 {@code non-null;} another list to compare + * @return whether the two lists contain corresponding equal elements + */ + public static boolean equalContents(TypeList list1, TypeList list2) { + int size = list1.size(); + + if (list2.size() != size) { + return false; } - /** {@inheritDoc} */ - public Type getType(int n) { - return get(n); + for (int i = 0; i < size; i++) { + if (!list1.getType(i).equals(list2.getType(i))) { + return false; + } } - /** {@inheritDoc} */ - public int getWordCount() { - int sz = size(); - int result = 0; - - for (int i = 0; i < sz; i++) { - result += get(i).getCategory(); - } + return true; + } + + /** + * Compares the contents of the given two instances for ordering. This + * is a static method so as to work on arbitrary {@link TypeList} + * instances. + * + * @param list1 {@code non-null;} one list to compare + * @param list2 {@code non-null;} another list to compare + * @return the order of the two lists + */ + public static int compareContents(TypeList list1, TypeList list2) { + int size1 = list1.size(); + int size2 = list2.size(); + int size = Math.min(size1, size2); + + for (int i = 0; i < size; i++) { + int comparison = list1.getType(i).compareTo(list2.getType(i)); + if (comparison != 0) { + return comparison; + } + } - return result; + if (size1 == size2) { + return 0; + } else if (size1 < size2) { + return -1; + } else { + return 1; + } + } + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size the size of the list + */ + public StdTypeList(int size) { + super(size); + } + + /** {@inheritDoc} */ + @Override + public Type getType(int n) { + return get(n); + } + + /** {@inheritDoc} */ + @Override + public int getWordCount() { + int sz = size(); + int result = 0; + + for (int i = 0; i < sz; i++) { + result += get(i).getCategory(); } - /** {@inheritDoc} */ - public TypeList withAddedType(Type type) { - int sz = size(); - StdTypeList result = new StdTypeList(sz + 1); + return result; + } - for (int i = 0; i < sz; i++) { - result.set0(i, get0(i)); - } + /** {@inheritDoc} */ + @Override + public TypeList withAddedType(Type type) { + int sz = size(); + StdTypeList result = new StdTypeList(sz + 1); - result.set(sz, type); - result.setImmutable(); - return result; + for (int i = 0; i < sz; i++) { + result.set0(i, get0(i)); } - /** - * Gets the indicated element. It is an error to call this with the - * index for an element which was never set; if you do that, this - * will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which element - * @return {@code non-null;} the indicated element - */ - public Type get(int n) { - return (Type) get0(n); + result.set(sz, type); + result.setImmutable(); + return result; + } + + /** + * Gets the indicated element. It is an error to call this with the + * index for an element which was never set; if you do that, this + * will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element + */ + public Type get(int n) { + return (Type) get0(n); + } + + /** + * Sets the type at the given index. + * + * @param n {@code >= 0, < size();} which element + * @param type {@code non-null;} the type to store + */ + public void set(int n, Type type) { + set0(n, type); + } + + /** + * Returns a new instance, which is the same as this instance, + * except that it has an additional type prepended to the + * original. + * + * @param type {@code non-null;} the new first element + * @return {@code non-null;} an appropriately-constructed instance + */ + public StdTypeList withFirst(Type type) { + int sz = size(); + StdTypeList result = new StdTypeList(sz + 1); + + result.set0(0, type); + for (int i = 0; i < sz; i++) { + result.set0(i + 1, getOrNull0(i)); } - /** - * Sets the type at the given index. - * - * @param n {@code >= 0, < size();} which element - * @param type {@code non-null;} the type to store - */ - public void set(int n, Type type) { - set0(n, type); - } - - /** - * Returns a new instance, which is the same as this instance, - * except that it has an additional type prepended to the - * original. - * - * @param type {@code non-null;} the new first element - * @return {@code non-null;} an appropriately-constructed instance - */ - public StdTypeList withFirst(Type type) { - int sz = size(); - StdTypeList result = new StdTypeList(sz + 1); - - result.set0(0, type); - for (int i = 0; i < sz; i++) { - result.set0(i + 1, getOrNull0(i)); - } - - return result; - } + return result; + } } diff --git a/dx/src/com/android/jack/dx/rop/type/Type.java b/dx/src/com/android/jack/dx/rop/type/Type.java index 2b8d0a8..c6ea1fd 100644 --- a/dx/src/com/android/jack/dx/rop/type/Type.java +++ b/dx/src/com/android/jack/dx/rop/type/Type.java @@ -27,836 +27,844 @@ import java.util.HashMap; * other using {@code ==}. */ public final class Type implements TypeBearer, Comparable<Type> { - /** - * {@code non-null;} intern table mapping string descriptors to - * instances - */ - private static final HashMap<String, Type> internTable = - new HashMap<String, Type>(500); - - /** basic type constant for {@code void} */ - public static final int BT_VOID = 0; - - /** basic type constant for {@code boolean} */ - public static final int BT_BOOLEAN = 1; - - /** basic type constant for {@code byte} */ - public static final int BT_BYTE = 2; + /** + * {@code non-null;} intern table mapping string descriptors to + * instances + */ + private static final HashMap<String, Type> internTable = new HashMap<String, Type>(500); - /** basic type constant for {@code char} */ - public static final int BT_CHAR = 3; + /** basic type constant for {@code void} */ + public static final int BT_VOID = 0; - /** basic type constant for {@code double} */ - public static final int BT_DOUBLE = 4; + /** basic type constant for {@code boolean} */ + public static final int BT_BOOLEAN = 1; - /** basic type constant for {@code float} */ - public static final int BT_FLOAT = 5; + /** basic type constant for {@code byte} */ + public static final int BT_BYTE = 2; - /** basic type constant for {@code int} */ - public static final int BT_INT = 6; + /** basic type constant for {@code char} */ + public static final int BT_CHAR = 3; - /** basic type constant for {@code long} */ - public static final int BT_LONG = 7; + /** basic type constant for {@code double} */ + public static final int BT_DOUBLE = 4; - /** basic type constant for {@code short} */ - public static final int BT_SHORT = 8; + /** basic type constant for {@code float} */ + public static final int BT_FLOAT = 5; - /** basic type constant for {@code Object} */ - public static final int BT_OBJECT = 9; + /** basic type constant for {@code int} */ + public static final int BT_INT = 6; - /** basic type constant for a return address */ - public static final int BT_ADDR = 10; + /** basic type constant for {@code long} */ + public static final int BT_LONG = 7; - /** count of basic type constants */ - public static final int BT_COUNT = 11; + /** basic type constant for {@code short} */ + public static final int BT_SHORT = 8; - /** {@code non-null;} instance representing {@code boolean} */ - public static final Type BOOLEAN = new Type("Z", BT_BOOLEAN); + /** basic type constant for {@code Object} */ + public static final int BT_OBJECT = 9; - /** {@code non-null;} instance representing {@code byte} */ - public static final Type BYTE = new Type("B", BT_BYTE); + /** basic type constant for a return address */ + public static final int BT_ADDR = 10; - /** {@code non-null;} instance representing {@code char} */ - public static final Type CHAR = new Type("C", BT_CHAR); + /** count of basic type constants */ + public static final int BT_COUNT = 11; - /** {@code non-null;} instance representing {@code double} */ - public static final Type DOUBLE = new Type("D", BT_DOUBLE); + /** {@code non-null;} instance representing {@code boolean} */ + public static final Type BOOLEAN = new Type("Z", BT_BOOLEAN); - /** {@code non-null;} instance representing {@code float} */ - public static final Type FLOAT = new Type("F", BT_FLOAT); + /** {@code non-null;} instance representing {@code byte} */ + public static final Type BYTE = new Type("B", BT_BYTE); - /** {@code non-null;} instance representing {@code int} */ - public static final Type INT = new Type("I", BT_INT); + /** {@code non-null;} instance representing {@code char} */ + public static final Type CHAR = new Type("C", BT_CHAR); - /** {@code non-null;} instance representing {@code long} */ - public static final Type LONG = new Type("J", BT_LONG); + /** {@code non-null;} instance representing {@code double} */ + public static final Type DOUBLE = new Type("D", BT_DOUBLE); - /** {@code non-null;} instance representing {@code short} */ - public static final Type SHORT = new Type("S", BT_SHORT); + /** {@code non-null;} instance representing {@code float} */ + public static final Type FLOAT = new Type("F", BT_FLOAT); - /** {@code non-null;} instance representing {@code void} */ - public static final Type VOID = new Type("V", BT_VOID); + /** {@code non-null;} instance representing {@code int} */ + public static final Type INT = new Type("I", BT_INT); - /** {@code non-null;} instance representing a known-{@code null} */ - public static final Type KNOWN_NULL = new Type("<null>", BT_OBJECT); + /** {@code non-null;} instance representing {@code long} */ + public static final Type LONG = new Type("J", BT_LONG); - /** {@code non-null;} instance representing a subroutine return address */ - public static final Type RETURN_ADDRESS = new Type("<addr>", BT_ADDR); + /** {@code non-null;} instance representing {@code short} */ + public static final Type SHORT = new Type("S", BT_SHORT); + + /** {@code non-null;} instance representing {@code void} */ + public static final Type VOID = new Type("V", BT_VOID); + + /** {@code non-null;} instance representing a known-{@code null} */ + public static final Type KNOWN_NULL = new Type("<null>", BT_OBJECT); + + /** {@code non-null;} instance representing a subroutine return address */ + public static final Type RETURN_ADDRESS = new Type("<addr>", BT_ADDR); + + static { + /* + * Put all the primitive types into the intern table. This needs + * to happen before the array types below get interned. + */ + putIntern(BOOLEAN); + putIntern(BYTE); + putIntern(CHAR); + putIntern(DOUBLE); + putIntern(FLOAT); + putIntern(INT); + putIntern(LONG); + putIntern(SHORT); + /* + * Note: VOID isn't put in the intern table, since it's special and + * shouldn't be found by a normal call to intern(). + */ + } + + /** + * {@code non-null;} instance representing + * {@code java.lang.annotation.Annotation} + */ + public static final Type ANNOTATION = intern("Ljava/lang/annotation/Annotation;"); + + /** {@code non-null;} instance representing {@code java.lang.Class} */ + public static final Type CLASS = intern("Ljava/lang/Class;"); - static { - /* - * Put all the primitive types into the intern table. This needs - * to happen before the array types below get interned. - */ - putIntern(BOOLEAN); - putIntern(BYTE); - putIntern(CHAR); - putIntern(DOUBLE); - putIntern(FLOAT); - putIntern(INT); - putIntern(LONG); - putIntern(SHORT); - /* - * Note: VOID isn't put in the intern table, since it's special and - * shouldn't be found by a normal call to intern(). - */ - } - - /** - * {@code non-null;} instance representing - * {@code java.lang.annotation.Annotation} - */ - public static final Type ANNOTATION = - intern("Ljava/lang/annotation/Annotation;"); + /** {@code non-null;} instance representing {@code java.lang.Cloneable} */ + public static final Type CLONEABLE = intern("Ljava/lang/Cloneable;"); - /** {@code non-null;} instance representing {@code java.lang.Class} */ - public static final Type CLASS = intern("Ljava/lang/Class;"); + /** {@code non-null;} instance representing {@code java.lang.Object} */ + public static final Type OBJECT = intern("Ljava/lang/Object;"); - /** {@code non-null;} instance representing {@code java.lang.Cloneable} */ - public static final Type CLONEABLE = intern("Ljava/lang/Cloneable;"); - - /** {@code non-null;} instance representing {@code java.lang.Object} */ - public static final Type OBJECT = intern("Ljava/lang/Object;"); - - /** {@code non-null;} instance representing {@code java.io.Serializable} */ - public static final Type SERIALIZABLE = intern("Ljava/io/Serializable;"); - - /** {@code non-null;} instance representing {@code java.lang.String} */ - public static final Type STRING = intern("Ljava/lang/String;"); - - /** {@code non-null;} instance representing {@code java.lang.Throwable} */ - public static final Type THROWABLE = intern("Ljava/lang/Throwable;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Boolean}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type BOOLEAN_CLASS = intern("Ljava/lang/Boolean;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Byte}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type BYTE_CLASS = intern("Ljava/lang/Byte;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Character}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type CHARACTER_CLASS = intern("Ljava/lang/Character;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Double}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type DOUBLE_CLASS = intern("Ljava/lang/Double;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Float}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type FLOAT_CLASS = intern("Ljava/lang/Float;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Integer}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type INTEGER_CLASS = intern("Ljava/lang/Integer;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Long}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type LONG_CLASS = intern("Ljava/lang/Long;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Short}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type SHORT_CLASS = intern("Ljava/lang/Short;"); - - /** - * {@code non-null;} instance representing {@code java.lang.Void}; the - * suffix on the name helps disambiguate this from the instance - * representing a primitive type - */ - public static final Type VOID_CLASS = intern("Ljava/lang/Void;"); - - /** {@code non-null;} instance representing {@code boolean[]} */ - public static final Type BOOLEAN_ARRAY = BOOLEAN.getArrayType(); - - /** {@code non-null;} instance representing {@code byte[]} */ - public static final Type BYTE_ARRAY = BYTE.getArrayType(); - - /** {@code non-null;} instance representing {@code char[]} */ - public static final Type CHAR_ARRAY = CHAR.getArrayType(); - - /** {@code non-null;} instance representing {@code double[]} */ - public static final Type DOUBLE_ARRAY = DOUBLE.getArrayType(); - - /** {@code non-null;} instance representing {@code float[]} */ - public static final Type FLOAT_ARRAY = FLOAT.getArrayType(); - - /** {@code non-null;} instance representing {@code int[]} */ - public static final Type INT_ARRAY = INT.getArrayType(); - - /** {@code non-null;} instance representing {@code long[]} */ - public static final Type LONG_ARRAY = LONG.getArrayType(); - - /** {@code non-null;} instance representing {@code Object[]} */ - public static final Type OBJECT_ARRAY = OBJECT.getArrayType(); - - /** {@code non-null;} instance representing {@code short[]} */ - public static final Type SHORT_ARRAY = SHORT.getArrayType(); - - /** {@code non-null;} field descriptor for the type */ - private final String descriptor; - - /** - * basic type corresponding to this type; one of the - * {@code BT_*} constants - */ - private final int basicType; - - /** - * {@code >= -1;} for an uninitialized type, bytecode index that this - * instance was allocated at; {@code Integer.MAX_VALUE} if it - * was an incoming uninitialized instance; {@code -1} if this - * is an <i>inititialized</i> instance - */ - private final int newAt; - - /** - * {@code null-ok;} the internal-form class name corresponding to - * this type, if calculated; only valid if {@code this} is a - * reference type and additionally not a return address - */ - private String className; - - /** - * {@code null-ok;} the type corresponding to an array of this type, if - * calculated - */ - private Type arrayType; - - /** - * {@code null-ok;} the type corresponding to elements of this type, if - * calculated; only valid if {@code this} is an array type - */ - private Type componentType; + /** {@code non-null;} instance representing {@code java.io.Serializable} */ + public static final Type SERIALIZABLE = intern("Ljava/io/Serializable;"); - /** - * {@code null-ok;} the type corresponding to the initialized version of - * this type, if this instance is in fact an uninitialized type - */ - private Type initializedType; - - /** - * Returns the unique instance corresponding to the type with the - * given descriptor. See vmspec-2 sec4.3.2 for details on the - * field descriptor syntax. This method does <i>not</i> allow - * {@code "V"} (that is, type {@code void}) as a valid - * descriptor. - * - * @param descriptor {@code non-null;} the descriptor - * @return {@code non-null;} the corresponding instance - * @throws IllegalArgumentException thrown if the descriptor has - * invalid syntax - */ - public static Type intern(String descriptor) { - Type result; - synchronized (internTable) { - result = internTable.get(descriptor); - } - if (result != null) { - return result; - } + /** {@code non-null;} instance representing {@code java.lang.String} */ + public static final Type STRING = intern("Ljava/lang/String;"); - char firstChar; - try { - firstChar = descriptor.charAt(0); - } catch (IndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("descriptor is empty"); - } catch (NullPointerException ex) { - // Elucidate the exception. - throw new NullPointerException("descriptor == null"); - } + /** {@code non-null;} instance representing {@code java.lang.Throwable} */ + public static final Type THROWABLE = intern("Ljava/lang/Throwable;"); - if (firstChar == '[') { - /* - * Recursively strip away array markers to get at the underlying - * type, and build back on to form the result. - */ - result = intern(descriptor.substring(1)); - return result.getArrayType(); + /** + * {@code non-null;} instance representing {@code java.lang.Boolean}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type BOOLEAN_CLASS = intern("Ljava/lang/Boolean;"); + + /** + * {@code non-null;} instance representing {@code java.lang.Byte}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type BYTE_CLASS = intern("Ljava/lang/Byte;"); + + /** + * {@code non-null;} instance representing {@code java.lang.Character}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type CHARACTER_CLASS = intern("Ljava/lang/Character;"); + + /** + * {@code non-null;} instance representing {@code java.lang.Double}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type DOUBLE_CLASS = intern("Ljava/lang/Double;"); + + /** + * {@code non-null;} instance representing {@code java.lang.Float}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type FLOAT_CLASS = intern("Ljava/lang/Float;"); + + /** + * {@code non-null;} instance representing {@code java.lang.Integer}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type INTEGER_CLASS = intern("Ljava/lang/Integer;"); + + /** + * {@code non-null;} instance representing {@code java.lang.Long}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type LONG_CLASS = intern("Ljava/lang/Long;"); + + /** + * {@code non-null;} instance representing {@code java.lang.Short}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type SHORT_CLASS = intern("Ljava/lang/Short;"); + + /** + * {@code non-null;} instance representing {@code java.lang.Void}; the + * suffix on the name helps disambiguate this from the instance + * representing a primitive type + */ + public static final Type VOID_CLASS = intern("Ljava/lang/Void;"); + + /** {@code non-null;} instance representing {@code boolean[]} */ + public static final Type BOOLEAN_ARRAY = BOOLEAN.getArrayType(); + + /** {@code non-null;} instance representing {@code byte[]} */ + public static final Type BYTE_ARRAY = BYTE.getArrayType(); + + /** {@code non-null;} instance representing {@code char[]} */ + public static final Type CHAR_ARRAY = CHAR.getArrayType(); + + /** {@code non-null;} instance representing {@code double[]} */ + public static final Type DOUBLE_ARRAY = DOUBLE.getArrayType(); + + /** {@code non-null;} instance representing {@code float[]} */ + public static final Type FLOAT_ARRAY = FLOAT.getArrayType(); + + /** {@code non-null;} instance representing {@code int[]} */ + public static final Type INT_ARRAY = INT.getArrayType(); + + /** {@code non-null;} instance representing {@code long[]} */ + public static final Type LONG_ARRAY = LONG.getArrayType(); + + /** {@code non-null;} instance representing {@code Object[]} */ + public static final Type OBJECT_ARRAY = OBJECT.getArrayType(); + + /** {@code non-null;} instance representing {@code short[]} */ + public static final Type SHORT_ARRAY = SHORT.getArrayType(); + + /** {@code non-null;} field descriptor for the type */ + private final String descriptor; + + /** + * basic type corresponding to this type; one of the + * {@code BT_*} constants + */ + private final int basicType; + + /** + * {@code >= -1;} for an uninitialized type, bytecode index that this + * instance was allocated at; {@code Integer.MAX_VALUE} if it + * was an incoming uninitialized instance; {@code -1} if this + * is an <i>inititialized</i> instance + */ + private final int newAt; + + /** + * {@code null-ok;} the internal-form class name corresponding to + * this type, if calculated; only valid if {@code this} is a + * reference type and additionally not a return address + */ + private String className; + + /** + * {@code null-ok;} the type corresponding to an array of this type, if + * calculated + */ + private Type arrayType; + + /** + * {@code null-ok;} the type corresponding to elements of this type, if + * calculated; only valid if {@code this} is an array type + */ + private Type componentType; + + /** + * {@code null-ok;} the type corresponding to the initialized version of + * this type, if this instance is in fact an uninitialized type + */ + private Type initializedType; + + /** + * Returns the unique instance corresponding to the type with the + * given descriptor. See vmspec-2 sec4.3.2 for details on the + * field descriptor syntax. This method does <i>not</i> allow + * {@code "V"} (that is, type {@code void}) as a valid + * descriptor. + * + * @param descriptor {@code non-null;} the descriptor + * @return {@code non-null;} the corresponding instance + * @throws IllegalArgumentException thrown if the descriptor has + * invalid syntax + */ + public static Type intern(String descriptor) { + Type result; + synchronized (internTable) { + result = internTable.get(descriptor); + } + if (result != null) { + return result; + } + + char firstChar; + try { + firstChar = descriptor.charAt(0); + } catch (IndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("descriptor is empty"); + } catch (NullPointerException ex) { + // Elucidate the exception. + throw new NullPointerException("descriptor == null"); + } + + if (firstChar == '[') { + /* + * Recursively strip away array markers to get at the underlying + * type, and build back on to form the result. + */ + result = intern(descriptor.substring(1)); + return result.getArrayType(); + } + + /* + * If the first character isn't '[' and it wasn't found in the + * intern cache, then it had better be the descriptor for a class. + */ + +int length = descriptor.length(); + if ((firstChar != 'L') || (descriptor.charAt(length - 1) != ';')) { + throw new IllegalArgumentException("bad descriptor: " + descriptor); + } + + /* + * Validate the characters of the class name itself. Note that + * vmspec-2 does not have a coherent definition for valid + * internal-form class names, and the definition here is fairly + * liberal: A name is considered valid as long as it doesn't + * contain any of '[' ';' '.' '(' ')', and it has no more than one + * '/' in a row, and no '/' at either end. + */ + +int limit = (length - 1); // Skip the final ';'. + for (int i = 1; i < limit; i++) { + char c = descriptor.charAt(i); + switch (c) { + case '[': + case ';': + case '.': + case '(': + case ')': { + throw new IllegalArgumentException("bad descriptor: " + descriptor); } - - /* - * If the first character isn't '[' and it wasn't found in the - * intern cache, then it had better be the descriptor for a class. - */ - - int length = descriptor.length(); - if ((firstChar != 'L') || - (descriptor.charAt(length - 1) != ';')) { + case '/': { + if ((i == 1) || (i == (length - 1)) || (descriptor.charAt(i - 1) == '/')) { throw new IllegalArgumentException("bad descriptor: " + descriptor); + } + break; } - - /* - * Validate the characters of the class name itself. Note that - * vmspec-2 does not have a coherent definition for valid - * internal-form class names, and the definition here is fairly - * liberal: A name is considered valid as long as it doesn't - * contain any of '[' ';' '.' '(' ')', and it has no more than one - * '/' in a row, and no '/' at either end. - */ - - int limit = (length - 1); // Skip the final ';'. - for (int i = 1; i < limit; i++) { - char c = descriptor.charAt(i); - switch (c) { - case '[': - case ';': - case '.': - case '(': - case ')': { - throw new IllegalArgumentException("bad descriptor: " + descriptor); - } - case '/': { - if ((i == 1) || - (i == (length - 1)) || - (descriptor.charAt(i - 1) == '/')) { - throw new IllegalArgumentException("bad descriptor: " + descriptor); - } - break; - } - } - } - - result = new Type(descriptor, BT_OBJECT); - return putIntern(result); - } - - /** - * Returns the unique instance corresponding to the type with the - * given descriptor, allowing {@code "V"} to return the type - * for {@code void}. Other than that one caveat, this method - * is identical to {@link #intern}. - * - * @param descriptor {@code non-null;} the descriptor - * @return {@code non-null;} the corresponding instance - * @throws IllegalArgumentException thrown if the descriptor has - * invalid syntax - */ - public static Type internReturnType(String descriptor) { - try { - if (descriptor.equals("V")) { - // This is the one special case where void may be returned. - return VOID; - } - } catch (NullPointerException ex) { - // Elucidate the exception. - throw new NullPointerException("descriptor == null"); - } - - return intern(descriptor); - } - - /** - * Returns the unique instance corresponding to the type of the - * class with the given name. Calling this method is equivalent to - * calling {@code intern(name)} if {@code name} begins - * with {@code "["} and calling {@code intern("L" + name + ";")} - * in all other cases. - * - * @param name {@code non-null;} the name of the class whose type - * is desired - * @return {@code non-null;} the corresponding type - * @throws IllegalArgumentException thrown if the name has - * invalid syntax - */ - public static Type internClassName(String name) { - if (name == null) { - throw new NullPointerException("name == null"); - } - - if (name.startsWith("[")) { - return intern(name); - } - - return intern('L' + name + ';'); - } - - /** - * Constructs an instance corresponding to an "uninitialized type." - * This is a private constructor; use one of the public static - * methods to get instances. - * - * @param descriptor {@code non-null;} the field descriptor for the type - * @param basicType basic type corresponding to this type; one of the - * {@code BT_*} constants - * @param newAt {@code >= -1;} allocation bytecode index - */ - private Type(String descriptor, int basicType, int newAt) { - if (descriptor == null) { - throw new NullPointerException("descriptor == null"); - } - - if ((basicType < 0) || (basicType >= BT_COUNT)) { - throw new IllegalArgumentException("bad basicType"); - } - - if (newAt < -1) { - throw new IllegalArgumentException("newAt < -1"); - } - - this.descriptor = descriptor; - this.basicType = basicType; - this.newAt = newAt; - this.arrayType = null; - this.componentType = null; - this.initializedType = null; - } - - /** - * Constructs an instance corresponding to an "initialized type." - * This is a private constructor; use one of the public static - * methods to get instances. - * - * @param descriptor {@code non-null;} the field descriptor for the type - * @param basicType basic type corresponding to this type; one of the - * {@code BT_*} constants - */ - private Type(String descriptor, int basicType) { - this(descriptor, basicType, -1); - } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (this == other) { - /* - * Since externally-visible types are interned, this check - * helps weed out some easy cases. - */ - return true; - } - - if (!(other instanceof Type)) { - return false; - } - - return descriptor.equals(((Type) other).descriptor); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return descriptor.hashCode(); - } - - /** {@inheritDoc} */ - public int compareTo(Type other) { - return descriptor.compareTo(other.descriptor); - } - - /** {@inheritDoc} */ - @Override - public String toString() { + } + } + + result = new Type(descriptor, BT_OBJECT); + return putIntern(result); + } + + /** + * Returns the unique instance corresponding to the type with the + * given descriptor, allowing {@code "V"} to return the type + * for {@code void}. Other than that one caveat, this method + * is identical to {@link #intern}. + * + * @param descriptor {@code non-null;} the descriptor + * @return {@code non-null;} the corresponding instance + * @throws IllegalArgumentException thrown if the descriptor has + * invalid syntax + */ + public static Type internReturnType(String descriptor) { + try { + if (descriptor.equals("V")) { + // This is the one special case where void may be returned. + return VOID; + } + } catch (NullPointerException ex) { + // Elucidate the exception. + throw new NullPointerException("descriptor == null"); + } + + return intern(descriptor); + } + + /** + * Returns the unique instance corresponding to the type of the + * class with the given name. Calling this method is equivalent to + * calling {@code intern(name)} if {@code name} begins + * with {@code "["} and calling {@code intern("L" + name + ";")} + * in all other cases. + * + * @param name {@code non-null;} the name of the class whose type + * is desired + * @return {@code non-null;} the corresponding type + * @throws IllegalArgumentException thrown if the name has + * invalid syntax + */ + public static Type internClassName(String name) { + if (name == null) { + throw new NullPointerException("name == null"); + } + + if (name.startsWith("[")) { + return intern(name); + } + + return intern('L' + name + ';'); + } + + /** + * Constructs an instance corresponding to an "uninitialized type." + * This is a private constructor; use one of the public static + * methods to get instances. + * + * @param descriptor {@code non-null;} the field descriptor for the type + * @param basicType basic type corresponding to this type; one of the + * {@code BT_*} constants + * @param newAt {@code >= -1;} allocation bytecode index + */ + private Type(String descriptor, int basicType, int newAt) { + if (descriptor == null) { + throw new NullPointerException("descriptor == null"); + } + + if ((basicType < 0) || (basicType >= BT_COUNT)) { + throw new IllegalArgumentException("bad basicType"); + } + + if (newAt < -1) { + throw new IllegalArgumentException("newAt < -1"); + } + + this.descriptor = descriptor; + this.basicType = basicType; + this.newAt = newAt; + this.arrayType = null; + this.componentType = null; + this.initializedType = null; + } + + /** + * Constructs an instance corresponding to an "initialized type." + * This is a private constructor; use one of the public static + * methods to get instances. + * + * @param descriptor {@code non-null;} the field descriptor for the type + * @param basicType basic type corresponding to this type; one of the + * {@code BT_*} constants + */ + private Type(String descriptor, int basicType) { + this(descriptor, basicType, -1); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (this == other) { + /* + * Since externally-visible types are interned, this check + * helps weed out some easy cases. + */ + return true; + } + + if (!(other instanceof Type)) { + return false; + } + + return descriptor.equals(((Type) other).descriptor); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return descriptor.hashCode(); + } + + /** {@inheritDoc} */ + @Override + public int compareTo(Type other) { + return descriptor.compareTo(other.descriptor); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return descriptor; + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + switch (basicType) { + case BT_VOID: + return "void"; + case BT_BOOLEAN: + return "boolean"; + case BT_BYTE: + return "byte"; + case BT_CHAR: + return "char"; + case BT_DOUBLE: + return "double"; + case BT_FLOAT: + return "float"; + case BT_INT: + return "int"; + case BT_LONG: + return "long"; + case BT_SHORT: + return "short"; + case BT_OBJECT: + break; + default: return descriptor; } - /** {@inheritDoc} */ - public String toHuman() { - switch (basicType) { - case BT_VOID: return "void"; - case BT_BOOLEAN: return "boolean"; - case BT_BYTE: return "byte"; - case BT_CHAR: return "char"; - case BT_DOUBLE: return "double"; - case BT_FLOAT: return "float"; - case BT_INT: return "int"; - case BT_LONG: return "long"; - case BT_SHORT: return "short"; - case BT_OBJECT: break; - default: return descriptor; - } - - if (isArray()) { - return getComponentType().toHuman() + "[]"; - } - - // Remove the "L...;" around the type and convert "/" to ".". - return getClassName().replace("/", "."); - } - - /** {@inheritDoc} */ - public Type getType() { - return this; - } - - /** {@inheritDoc} */ - public Type getFrameType() { - switch (basicType) { - case BT_BOOLEAN: - case BT_BYTE: - case BT_CHAR: - case BT_INT: - case BT_SHORT: { - return INT; - } - } - - return this; - } - - /** {@inheritDoc} */ - public int getBasicType() { - return basicType; - } - - /** {@inheritDoc} */ - public int getBasicFrameType() { - switch (basicType) { - case BT_BOOLEAN: - case BT_BYTE: - case BT_CHAR: - case BT_INT: - case BT_SHORT: { - return BT_INT; - } - } - - return basicType; - } - - /** {@inheritDoc} */ - public boolean isConstant() { + if (isArray()) { + return getComponentType().toHuman() + "[]"; + } + + // Remove the "L...;" around the type and convert "/" to ".". + return getClassName().replace("/", "."); + } + + /** {@inheritDoc} */ + @Override + public Type getType() { + return this; + } + + /** {@inheritDoc} */ + @Override + public Type getFrameType() { + switch (basicType) { + case BT_BOOLEAN: + case BT_BYTE: + case BT_CHAR: + case BT_INT: + case BT_SHORT: { + return INT; + } + } + + return this; + } + + /** {@inheritDoc} */ + @Override + public int getBasicType() { + return basicType; + } + + /** {@inheritDoc} */ + @Override + public int getBasicFrameType() { + switch (basicType) { + case BT_BOOLEAN: + case BT_BYTE: + case BT_CHAR: + case BT_INT: + case BT_SHORT: { + return BT_INT; + } + } + + return basicType; + } + + /** {@inheritDoc} */ + @Override + public boolean isConstant() { + return false; + } + + /** + * Gets the descriptor. + * + * @return {@code non-null;} the descriptor + */ + public String getDescriptor() { + return descriptor; + } + + /** + * Gets the name of the class this type corresponds to, in internal + * form. This method is only valid if this instance is for a + * normal reference type (that is, a reference type and + * additionally not a return address). + * + * @return {@code non-null;} the internal-form class name + */ + public String getClassName() { + if (className == null) { + if (!isReference()) { + throw new IllegalArgumentException("not an object type: " + descriptor); + } + + if (descriptor.charAt(0) == '[') { + className = descriptor; + } else { + className = descriptor.substring(1, descriptor.length() - 1); + } + } + + return className; + } + + /** + * Gets the category. Most instances are category 1. {@code long} + * and {@code double} are the only category 2 types. + * + * @see #isCategory1 + * @see #isCategory2 + * @return the category + */ + public int getCategory() { + switch (basicType) { + case BT_LONG: + case BT_DOUBLE: { + return 2; + } + } + + return 1; + } + + /** + * Returns whether or not this is a category 1 type. + * + * @see #getCategory + * @see #isCategory2 + * @return whether or not this is a category 1 type + */ + public boolean isCategory1() { + switch (basicType) { + case BT_LONG: + case BT_DOUBLE: { return false; - } - - /** - * Gets the descriptor. - * - * @return {@code non-null;} the descriptor - */ - public String getDescriptor() { - return descriptor; - } - - /** - * Gets the name of the class this type corresponds to, in internal - * form. This method is only valid if this instance is for a - * normal reference type (that is, a reference type and - * additionally not a return address). - * - * @return {@code non-null;} the internal-form class name - */ - public String getClassName() { - if (className == null) { - if (!isReference()) { - throw new IllegalArgumentException("not an object type: " + - descriptor); - } - - if (descriptor.charAt(0) == '[') { - className = descriptor; - } else { - className = descriptor.substring(1, descriptor.length() - 1); - } - } - - return className; - } - - /** - * Gets the category. Most instances are category 1. {@code long} - * and {@code double} are the only category 2 types. - * - * @see #isCategory1 - * @see #isCategory2 - * @return the category - */ - public int getCategory() { - switch (basicType) { - case BT_LONG: - case BT_DOUBLE: { - return 2; - } - } - - return 1; - } - - /** - * Returns whether or not this is a category 1 type. - * - * @see #getCategory - * @see #isCategory2 - * @return whether or not this is a category 1 type - */ - public boolean isCategory1() { - switch (basicType) { - case BT_LONG: - case BT_DOUBLE: { - return false; - } - } - + } + } + + return true; + } + + /** + * Returns whether or not this is a category 2 type. + * + * @see #getCategory + * @see #isCategory1 + * @return whether or not this is a category 2 type + */ + public boolean isCategory2() { + switch (basicType) { + case BT_LONG: + case BT_DOUBLE: { return true; - } - - /** - * Returns whether or not this is a category 2 type. - * - * @see #getCategory - * @see #isCategory1 - * @return whether or not this is a category 2 type - */ - public boolean isCategory2() { - switch (basicType) { - case BT_LONG: - case BT_DOUBLE: { - return true; - } - } - - return false; - } - - /** - * Gets whether this type is "intlike." An intlike type is one which, when - * placed on a stack or in a local, is automatically converted to an - * {@code int}. - * - * @return whether this type is "intlike" - */ - public boolean isIntlike() { - switch (basicType) { - case BT_BOOLEAN: - case BT_BYTE: - case BT_CHAR: - case BT_INT: - case BT_SHORT: { - return true; - } - } - - return false; - } - - /** - * Gets whether this type is a primitive type. All types are either - * primitive or reference types. - * - * @return whether this type is primitive - */ - public boolean isPrimitive() { - switch (basicType) { - case BT_BOOLEAN: - case BT_BYTE: - case BT_CHAR: - case BT_DOUBLE: - case BT_FLOAT: - case BT_INT: - case BT_LONG: - case BT_SHORT: - case BT_VOID: { - return true; - } - } - - return false; - } - - /** - * Gets whether this type is a normal reference type. A normal - * reference type is a reference type that is not a return - * address. This method is just convenient shorthand for - * {@code getBasicType() == Type.BT_OBJECT}. - * - * @return whether this type is a normal reference type - */ - public boolean isReference() { - return (basicType == BT_OBJECT); - } - - /** - * Gets whether this type is an array type. If this method returns - * {@code true}, then it is safe to use {@link #getComponentType} - * to determine the component type. - * - * @return whether this type is an array type - */ - public boolean isArray() { - return (descriptor.charAt(0) == '['); - } - - /** - * Gets whether this type is an array type or is a known-null, and - * hence is compatible with array types. - * - * @return whether this type is an array type - */ - public boolean isArrayOrKnownNull() { - return isArray() || equals(KNOWN_NULL); - } - - /** - * Gets whether this type represents an uninitialized instance. An - * uninitialized instance is what one gets back from the {@code new} - * opcode, and remains uninitialized until a valid constructor is - * invoked on it. - * - * @return whether this type is "uninitialized" - */ - public boolean isUninitialized() { - return (newAt >= 0); - } - - /** - * Gets the bytecode index at which this uninitialized type was - * allocated. This returns {@code Integer.MAX_VALUE} if this - * type is an uninitialized incoming parameter (i.e., the - * {@code this} of an {@code <init>} method) or - * {@code -1} if this type is in fact <i>initialized</i>. - * - * @return {@code >= -1;} the allocation bytecode index - */ - public int getNewAt() { - return newAt; - } - - /** - * Gets the initialized type corresponding to this instance, but only - * if this instance is in fact an uninitialized object type. - * - * @return {@code non-null;} the initialized type - */ - public Type getInitializedType() { - if (initializedType == null) { - throw new IllegalArgumentException("initialized type: " + - descriptor); - } - - return initializedType; - } - - /** - * Gets the type corresponding to an array of this type. - * - * @return {@code non-null;} the array type - */ - public Type getArrayType() { - if (arrayType == null) { - arrayType = putIntern(new Type('[' + descriptor, BT_OBJECT)); - } - - return arrayType; - } - - /** - * Gets the component type of this type. This method is only valid on - * array types. - * - * @return {@code non-null;} the component type - */ - public Type getComponentType() { - if (componentType == null) { - if (descriptor.charAt(0) != '[') { - throw new IllegalArgumentException("not an array type: " + - descriptor); - } - componentType = intern(descriptor.substring(1)); - } - - return componentType; - } - - /** - * Returns a new interned instance which is identical to this one, except - * it is indicated as uninitialized and allocated at the given bytecode - * index. This instance must be an initialized object type. - * - * @param newAt {@code >= 0;} the allocation bytecode index - * @return {@code non-null;} an appropriately-constructed instance - */ - public Type asUninitialized(int newAt) { - if (newAt < 0) { - throw new IllegalArgumentException("newAt < 0"); - } - - if (!isReference()) { - throw new IllegalArgumentException("not a reference type: " + - descriptor); - } - - if (isUninitialized()) { - /* - * Dealing with uninitialized types as a starting point is - * a pain, and it's not clear that it'd ever be used, so - * just disallow it. - */ - throw new IllegalArgumentException("already uninitialized: " + - descriptor); - } - - /* - * Create a new descriptor that is unique and shouldn't conflict - * with "normal" type descriptors - */ - String newDesc = 'N' + Hex.u2(newAt) + descriptor; - Type result = new Type(newDesc, BT_OBJECT, newAt); - result.initializedType = this; - return putIntern(result); - } - - /** - * Puts the given instance in the intern table if it's not already - * there. If a conflicting value is already in the table, then leave it. - * Return the interned value. - * - * @param type {@code non-null;} instance to make interned - * @return {@code non-null;} the actual interned object - */ - private static Type putIntern(Type type) { - synchronized (internTable) { - String descriptor = type.getDescriptor(); - Type already = internTable.get(descriptor); - if (already != null) { - return already; - } - internTable.put(descriptor, type); - return type; - } - } + } + } + + return false; + } + + /** + * Gets whether this type is "intlike." An intlike type is one which, when + * placed on a stack or in a local, is automatically converted to an + * {@code int}. + * + * @return whether this type is "intlike" + */ + public boolean isIntlike() { + switch (basicType) { + case BT_BOOLEAN: + case BT_BYTE: + case BT_CHAR: + case BT_INT: + case BT_SHORT: { + return true; + } + } + + return false; + } + + /** + * Gets whether this type is a primitive type. All types are either + * primitive or reference types. + * + * @return whether this type is primitive + */ + public boolean isPrimitive() { + switch (basicType) { + case BT_BOOLEAN: + case BT_BYTE: + case BT_CHAR: + case BT_DOUBLE: + case BT_FLOAT: + case BT_INT: + case BT_LONG: + case BT_SHORT: + case BT_VOID: { + return true; + } + } + + return false; + } + + /** + * Gets whether this type is a normal reference type. A normal + * reference type is a reference type that is not a return + * address. This method is just convenient shorthand for + * {@code getBasicType() == Type.BT_OBJECT}. + * + * @return whether this type is a normal reference type + */ + public boolean isReference() { + return (basicType == BT_OBJECT); + } + + /** + * Gets whether this type is an array type. If this method returns + * {@code true}, then it is safe to use {@link #getComponentType} + * to determine the component type. + * + * @return whether this type is an array type + */ + public boolean isArray() { + return (descriptor.charAt(0) == '['); + } + + /** + * Gets whether this type is an array type or is a known-null, and + * hence is compatible with array types. + * + * @return whether this type is an array type + */ + public boolean isArrayOrKnownNull() { + return isArray() || equals(KNOWN_NULL); + } + + /** + * Gets whether this type represents an uninitialized instance. An + * uninitialized instance is what one gets back from the {@code new} + * opcode, and remains uninitialized until a valid constructor is + * invoked on it. + * + * @return whether this type is "uninitialized" + */ + public boolean isUninitialized() { + return (newAt >= 0); + } + + /** + * Gets the bytecode index at which this uninitialized type was + * allocated. This returns {@code Integer.MAX_VALUE} if this + * type is an uninitialized incoming parameter (i.e., the + * {@code this} of an {@code <init>} method) or + * {@code -1} if this type is in fact <i>initialized</i>. + * + * @return {@code >= -1;} the allocation bytecode index + */ + public int getNewAt() { + return newAt; + } + + /** + * Gets the initialized type corresponding to this instance, but only + * if this instance is in fact an uninitialized object type. + * + * @return {@code non-null;} the initialized type + */ + public Type getInitializedType() { + if (initializedType == null) { + throw new IllegalArgumentException("initialized type: " + descriptor); + } + + return initializedType; + } + + /** + * Gets the type corresponding to an array of this type. + * + * @return {@code non-null;} the array type + */ + public Type getArrayType() { + if (arrayType == null) { + arrayType = putIntern(new Type('[' + descriptor, BT_OBJECT)); + } + + return arrayType; + } + + /** + * Gets the component type of this type. This method is only valid on + * array types. + * + * @return {@code non-null;} the component type + */ + public Type getComponentType() { + if (componentType == null) { + if (descriptor.charAt(0) != '[') { + throw new IllegalArgumentException("not an array type: " + descriptor); + } + componentType = intern(descriptor.substring(1)); + } + + return componentType; + } + + /** + * Returns a new interned instance which is identical to this one, except + * it is indicated as uninitialized and allocated at the given bytecode + * index. This instance must be an initialized object type. + * + * @param newAt {@code >= 0;} the allocation bytecode index + * @return {@code non-null;} an appropriately-constructed instance + */ + public Type asUninitialized(int newAt) { + if (newAt < 0) { + throw new IllegalArgumentException("newAt < 0"); + } + + if (!isReference()) { + throw new IllegalArgumentException("not a reference type: " + descriptor); + } + + if (isUninitialized()) { + /* + * Dealing with uninitialized types as a starting point is + * a pain, and it's not clear that it'd ever be used, so + * just disallow it. + */ + throw new IllegalArgumentException("already uninitialized: " + descriptor); + } + + /* + * Create a new descriptor that is unique and shouldn't conflict + * with "normal" type descriptors + */ + String newDesc = 'N' + Hex.u2(newAt) + descriptor; + Type result = new Type(newDesc, BT_OBJECT, newAt); + result.initializedType = this; + return putIntern(result); + } + + /** + * Puts the given instance in the intern table if it's not already + * there. If a conflicting value is already in the table, then leave it. + * Return the interned value. + * + * @param type {@code non-null;} instance to make interned + * @return {@code non-null;} the actual interned object + */ + private static Type putIntern(Type type) { + synchronized (internTable) { + String descriptor = type.getDescriptor(); + Type already = internTable.get(descriptor); + if (already != null) { + return already; + } + internTable.put(descriptor, type); + return type; + } + } } diff --git a/dx/src/com/android/jack/dx/rop/type/TypeBearer.java b/dx/src/com/android/jack/dx/rop/type/TypeBearer.java index 012814e..448e0e9 100644 --- a/dx/src/com/android/jack/dx/rop/type/TypeBearer.java +++ b/dx/src/com/android/jack/dx/rop/type/TypeBearer.java @@ -21,54 +21,53 @@ import com.android.jack.dx.util.ToHuman; /** * Object which has an associated type, possibly itself. */ -public interface TypeBearer - extends ToHuman { - /** - * Gets the type associated with this instance. - * - * @return {@code non-null;} the type - */ - public Type getType(); +public interface TypeBearer extends ToHuman { + /** + * Gets the type associated with this instance. + * + * @return {@code non-null;} the type + */ + public Type getType(); - /** - * Gets the frame type corresponding to this type. This method returns - * {@code this}, except if {@link Type#isIntlike} on the underlying - * type returns {@code true} but the underlying type is not in - * fact {@link Type#INT}, in which case this method returns an instance - * whose underlying type <i>is</i> {@code INT}. - * - * @return {@code non-null;} the frame type for this instance - */ - public TypeBearer getFrameType(); + /** + * Gets the frame type corresponding to this type. This method returns + * {@code this}, except if {@link Type#isIntlike} on the underlying + * type returns {@code true} but the underlying type is not in + * fact {@link Type#INT}, in which case this method returns an instance + * whose underlying type <i>is</i> {@code INT}. + * + * @return {@code non-null;} the frame type for this instance + */ + public TypeBearer getFrameType(); - /** - * Gets the basic type corresponding to this instance. - * - * @return the basic type; one of the {@code BT_*} constants - * defined by {@link Type} - */ - public int getBasicType(); + /** + * Gets the basic type corresponding to this instance. + * + * @return the basic type; one of the {@code BT_*} constants + * defined by {@link Type} + */ + public int getBasicType(); - /** - * Gets the basic type corresponding to this instance's frame type. This - * is equivalent to {@code getFrameType().getBasicType()}, and - * is the same as calling {@code getFrameType()} unless this - * instance is an int-like type, in which case this method returns - * {@code BT_INT}. - * - * @see #getBasicType - * @see #getFrameType - * - * @return the basic frame type; one of the {@code BT_*} constants - * defined by {@link Type} - */ - public int getBasicFrameType(); + /** + * Gets the basic type corresponding to this instance's frame type. This + * is equivalent to {@code getFrameType().getBasicType()}, and + * is the same as calling {@code getFrameType()} unless this + * instance is an int-like type, in which case this method returns + * {@code BT_INT}. + * + * @see #getBasicType + * @see #getFrameType + * + * @return the basic frame type; one of the {@code BT_*} constants + * defined by {@link Type} + */ + public int getBasicFrameType(); - /** - * Returns whether this instance represents a constant value. - * - * @return {@code true} if this instance represents a constant value - * and {@code false} if not - */ - public boolean isConstant(); + /** + * Returns whether this instance represents a constant value. + * + * @return {@code true} if this instance represents a constant value + * and {@code false} if not + */ + public boolean isConstant(); } diff --git a/dx/src/com/android/jack/dx/rop/type/TypeList.java b/dx/src/com/android/jack/dx/rop/type/TypeList.java index 825d881..48cc50a 100644 --- a/dx/src/com/android/jack/dx/rop/type/TypeList.java +++ b/dx/src/com/android/jack/dx/rop/type/TypeList.java @@ -20,50 +20,50 @@ package com.android.jack.dx.rop.type; * List of {@link Type} instances (or of things that contain types). */ public interface TypeList { - /** - * Returns whether this instance is mutable. Note that the - * {@code TypeList} interface itself doesn't provide any - * means of mutation, but that doesn't mean that there isn't an - * extra-interface way of mutating an instance. - * - * @return {@code true} if this instance is mutable or - * {@code false} if it is immutable - */ - public boolean isMutable(); + /** + * Returns whether this instance is mutable. Note that the + * {@code TypeList} interface itself doesn't provide any + * means of mutation, but that doesn't mean that there isn't an + * extra-interface way of mutating an instance. + * + * @return {@code true} if this instance is mutable or + * {@code false} if it is immutable + */ + public boolean isMutable(); - /** - * Gets the size of this list. - * - * @return {@code >= 0;} the size - */ - public int size(); + /** + * Gets the size of this list. + * + * @return {@code >= 0;} the size + */ + public int size(); - /** - * Gets the indicated element. It is an error to call this with the - * index for an element which was never set; if you do that, this - * will throw {@code NullPointerException}. - * - * @param n {@code >= 0, < size();} which element - * @return {@code non-null;} the indicated element - */ - public Type getType(int n); + /** + * Gets the indicated element. It is an error to call this with the + * index for an element which was never set; if you do that, this + * will throw {@code NullPointerException}. + * + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element + */ + public Type getType(int n); - /** - * Gets the number of 32-bit words required to hold instances of - * all the elements of this list. This is a sum of the widths (categories) - * of all the elements. - * - * @return {@code >= 0;} the required number of words - */ - public int getWordCount(); + /** + * Gets the number of 32-bit words required to hold instances of + * all the elements of this list. This is a sum of the widths (categories) + * of all the elements. + * + * @return {@code >= 0;} the required number of words + */ + public int getWordCount(); - /** - * Returns a new instance which is identical to this one, except that - * the given item is appended to the end and it is guaranteed to be - * immutable. - * - * @param type {@code non-null;} item to append - * @return {@code non-null;} an appropriately-constructed instance - */ - public TypeList withAddedType(Type type); + /** + * Returns a new instance which is identical to this one, except that + * the given item is appended to the end and it is guaranteed to be + * immutable. + * + * @param type {@code non-null;} item to append + * @return {@code non-null;} an appropriately-constructed instance + */ + public TypeList withAddedType(Type type); } diff --git a/dx/src/com/android/jack/dx/ssa/BasicRegisterMapper.java b/dx/src/com/android/jack/dx/ssa/BasicRegisterMapper.java index f9bdb2f..7093355 100644 --- a/dx/src/com/android/jack/dx/ssa/BasicRegisterMapper.java +++ b/dx/src/com/android/jack/dx/ssa/BasicRegisterMapper.java @@ -17,7 +17,6 @@ package com.android.jack.dx.ssa; import com.android.jack.dx.rop.code.RegisterSpec; -import com.android.jack.dx.rop.code.RegisterSpecList; import com.android.jack.dx.util.IntList; /** @@ -25,105 +24,105 @@ import com.android.jack.dx.util.IntList; * each mapping built up individually and added via addMapping() */ public class BasicRegisterMapper extends RegisterMapper { - /** indexed by old register, containing new name */ - private IntList oldToNew; - - /** running count of used registers in new namespace */ - private int runningCountNewRegisters; - - /** - * Creates a new OneToOneRegisterMapper. - * - * @param countOldRegisters the number of registers in the old name space - */ - public BasicRegisterMapper(int countOldRegisters) { - oldToNew = new IntList(countOldRegisters); + /** indexed by old register, containing new name */ + private IntList oldToNew; + + /** running count of used registers in new namespace */ + private int runningCountNewRegisters; + + /** + * Creates a new OneToOneRegisterMapper. + * + * @param countOldRegisters the number of registers in the old name space + */ + public BasicRegisterMapper(int countOldRegisters) { + oldToNew = new IntList(countOldRegisters); + } + + /** {@inheritDoc} */ + @Override + public int getNewRegisterCount() { + return runningCountNewRegisters; + } + + /** {@inheritDoc} */ + @Override + public RegisterSpec map(RegisterSpec registerSpec) { + if (registerSpec == null) { + return null; } - /** {@inheritDoc} */ - @Override - public int getNewRegisterCount() { - return runningCountNewRegisters; + int newReg; + try { + newReg = oldToNew.get(registerSpec.getReg()); + } catch (IndexOutOfBoundsException ex) { + newReg = -1; } - /** {@inheritDoc} */ - @Override - public RegisterSpec map(RegisterSpec registerSpec) { - if (registerSpec == null) { - return null; - } - - int newReg; - try { - newReg = oldToNew.get(registerSpec.getReg()); - } catch (IndexOutOfBoundsException ex) { - newReg = -1; - } - - if (newReg < 0) { - throw new RuntimeException("no mapping specified for register"); - } - - return registerSpec.withReg(newReg); + if (newReg < 0) { + throw new RuntimeException("no mapping specified for register"); } - /** - * Returns the new-namespace mapping for the specified - * old-namespace register, or -1 if one exists. - * - * @param oldReg {@code >= 0;} old-namespace register - * @return new-namespace register or -1 if none - */ - public int oldToNew(int oldReg) { - if (oldReg >= oldToNew.size()) { - return -1; - } - - return oldToNew.get(oldReg); + return registerSpec.withReg(newReg); + } + + /** + * Returns the new-namespace mapping for the specified + * old-namespace register, or -1 if one exists. + * + * @param oldReg {@code >= 0;} old-namespace register + * @return new-namespace register or -1 if none + */ + public int oldToNew(int oldReg) { + if (oldReg >= oldToNew.size()) { + return -1; } - /** {@inheritDoc} */ - public String toHuman() { - StringBuilder sb = new StringBuilder(); + return oldToNew.get(oldReg); + } - sb.append("Old\tNew\n"); - int sz = oldToNew.size(); + /** {@inheritDoc} */ + public String toHuman() { + StringBuilder sb = new StringBuilder(); - for (int i = 0; i < sz; i++) { - sb.append(i); - sb.append('\t'); - sb.append(oldToNew.get(i)); - sb.append('\n'); - } + sb.append("Old\tNew\n"); + int sz = oldToNew.size(); - sb.append("new reg count:"); - - sb.append(runningCountNewRegisters); - sb.append('\n'); + for (int i = 0; i < sz; i++) { + sb.append(i); + sb.append('\t'); + sb.append(oldToNew.get(i)); + sb.append('\n'); + } - return sb.toString(); + sb.append("new reg count:"); + + sb.append(runningCountNewRegisters); + sb.append('\n'); + + return sb.toString(); + } + + /** + * Adds a mapping to the mapper. If oldReg has already been mapped, + * overwrites previous mapping with new mapping. + * + * @param oldReg {@code >= 0;} old register + * @param newReg {@code >= 0;} new register + * @param category {@code 1..2;} width of reg + */ + public void addMapping(int oldReg, int newReg, int category) { + if (oldReg >= oldToNew.size()) { + // expand the array as necessary + for (int i = oldReg - oldToNew.size(); i >= 0; i--) { + oldToNew.add(-1); + } } - /** - * Adds a mapping to the mapper. If oldReg has already been mapped, - * overwrites previous mapping with new mapping. - * - * @param oldReg {@code >= 0;} old register - * @param newReg {@code >= 0;} new register - * @param category {@code 1..2;} width of reg - */ - public void addMapping(int oldReg, int newReg, int category) { - if (oldReg >= oldToNew.size()) { - // expand the array as necessary - for (int i = oldReg - oldToNew.size(); i >= 0; i--) { - oldToNew.add(-1); - } - } - - oldToNew.set(oldReg, newReg); - - if (runningCountNewRegisters < (newReg + category)) { - runningCountNewRegisters = newReg + category; - } + oldToNew.set(oldReg, newReg); + + if (runningCountNewRegisters < (newReg + category)) { + runningCountNewRegisters = newReg + category; } + } } diff --git a/dx/src/com/android/jack/dx/ssa/ConstCollector.java b/dx/src/com/android/jack/dx/ssa/ConstCollector.java index ee12cb4..aa73c2a 100644 --- a/dx/src/com/android/jack/dx/ssa/ConstCollector.java +++ b/dx/src/com/android/jack/dx/ssa/ConstCollector.java @@ -45,356 +45,340 @@ import java.util.Map; * insn size by about 3%. */ public class ConstCollector { - /** Maximum constants to collect per method. Puts cap on reg use */ - private static final int MAX_COLLECTED_CONSTANTS = 5; - - /** - * Also collect string consts, although they can throw exceptions. - * This is off now because it just doesn't seem to gain a whole lot. - * TODO if you turn this on, you must change SsaInsn.hasSideEffect() - * to return false for const-string insns whose exceptions are not - * caught in the current method. - */ - private static boolean COLLECT_STRINGS = false; + /** Maximum constants to collect per method. Puts cap on reg use */ + private static final int MAX_COLLECTED_CONSTANTS = 5; + + /** + * Also collect string consts, although they can throw exceptions. + * This is off now because it just doesn't seem to gain a whole lot. + * TODO(dx team) if you turn this on, you must change SsaInsn.hasSideEffect() + * to return false for const-string insns whose exceptions are not + * caught in the current method. + */ + private static boolean collectStrings = false; + + /** + * If true, allow one local var to be involved with a collected const. + * Turned off because it mostly just inserts more moves. + */ + private static final boolean COLLECT_ONE_LOCAL = false; + + /** method we're processing */ + private final SsaMethod ssaMeth; + + /** + * Processes a method. + * + * @param ssaMethod {@code non-null;} method to process + */ + public static void process(SsaMethod ssaMethod) { + ConstCollector cc = new ConstCollector(ssaMethod); + cc.run(); + } + + /** + * Constructs an instance. + * + * @param ssaMethod {@code non-null;} method to process + */ + private ConstCollector(SsaMethod ssaMethod) { + this.ssaMeth = ssaMethod; + } + + /** + * Applies the optimization. + */ + private void run() { + int regSz = ssaMeth.getRegCount(); + + ArrayList<TypedConstant> constantList = getConstsSortedByCountUse(); + + int toCollect = Math.min(constantList.size(), MAX_COLLECTED_CONSTANTS); + + SsaBasicBlock start = ssaMeth.getEntryBlock(); + + // Constant to new register containing the constant + HashMap<TypedConstant, RegisterSpec> newRegs = + new HashMap<TypedConstant, RegisterSpec>(toCollect); + + for (int i = 0; i < toCollect; i++) { + TypedConstant cst = constantList.get(i); + RegisterSpec result = RegisterSpec.make(ssaMeth.makeNewSsaReg(), cst); + + Rop constRop = Rops.opConst(cst); + + if (constRop.getBranchingness() == Rop.BRANCH_NONE) { + start.addInsnToHead(new PlainCstInsn(Rops.opConst(cst), SourcePosition.NO_INFO, result, + RegisterSpecList.EMPTY, cst)); + } else { + // We need two new basic blocks along with the new insn + SsaBasicBlock entryBlock = ssaMeth.getEntryBlock(); + SsaBasicBlock successorBlock = entryBlock.getPrimarySuccessor(); + + // Insert a block containing the const insn. + SsaBasicBlock constBlock = entryBlock.insertNewSuccessor(successorBlock); + + constBlock.replaceLastInsn(new ThrowingCstInsn(constRop, SourcePosition.NO_INFO, + RegisterSpecList.EMPTY, StdTypeList.EMPTY, cst)); + + // Insert a block containing the move-result-pseudo insn. + + SsaBasicBlock resultBlock = constBlock.insertNewSuccessor(successorBlock); + PlainInsn insn = new PlainInsn(Rops.opMoveResultPseudo(result.getTypeBearer()), + SourcePosition.NO_INFO, result, RegisterSpecList.EMPTY); + + resultBlock.addInsnToHead(insn); + } + + newRegs.put(cst, result); + } - /** - * If true, allow one local var to be involved with a collected const. - * Turned off because it mostly just inserts more moves. - */ - private static boolean COLLECT_ONE_LOCAL = false; + updateConstUses(newRegs, regSz); + } - /** method we're processing */ - private final SsaMethod ssaMeth; + /** + * Gets all of the collectable constant values used in this method, + * sorted by most used first. Skips non-collectable consts, such as + * non-string object constants + * + * @return {@code non-null;} list of constants in most-to-least used order + */ + private ArrayList<TypedConstant> getConstsSortedByCountUse() { + int regSz = ssaMeth.getRegCount(); - /** - * Processes a method. - * - * @param ssaMethod {@code non-null;} method to process - */ - public static void process(SsaMethod ssaMethod) { - ConstCollector cc = new ConstCollector(ssaMethod); - cc.run(); - } + final HashMap<TypedConstant, Integer> countUses = new HashMap<TypedConstant, Integer>(); - /** - * Constructs an instance. - * - * @param ssaMethod {@code non-null;} method to process + /* + * Each collected constant can be used by just one local + * (used only if COLLECT_ONE_LOCAL is true). */ - private ConstCollector(SsaMethod ssaMethod) { - this.ssaMeth = ssaMethod; - } + final HashSet<TypedConstant> usedByLocal = new HashSet<TypedConstant>(); - /** - * Applies the optimization. - */ - private void run() { - int regSz = ssaMeth.getRegCount(); + // Count how many times each const value is used. + for (int i = 0; i < regSz; i++) { + SsaInsn insn = ssaMeth.getDefinitionForRegister(i); - ArrayList<TypedConstant> constantList - = getConstsSortedByCountUse(); + if (insn == null || insn.getOpcode() == null) { + continue; + } - int toCollect = Math.min(constantList.size(), MAX_COLLECTED_CONSTANTS); + RegisterSpec result = insn.getResult(); + TypeBearer typeBearer = result.getTypeBearer(); - SsaBasicBlock start = ssaMeth.getEntryBlock(); + if (!typeBearer.isConstant()) { + continue; + } - // Constant to new register containing the constant - HashMap<TypedConstant, RegisterSpec> newRegs - = new HashMap<TypedConstant, RegisterSpec> (toCollect); + TypedConstant cst = (TypedConstant) typeBearer; - for (int i = 0; i < toCollect; i++) { - TypedConstant cst = constantList.get(i); - RegisterSpec result - = RegisterSpec.make(ssaMeth.makeNewSsaReg(), cst); + // Find defining instruction for move-result-pseudo instructions + if (insn.getOpcode().getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { + int pred = insn.getBlock().getPredecessors().nextSetBit(0); + ArrayList<SsaInsn> predInsns; + predInsns = ssaMeth.getBlocks().get(pred).getInsns(); + insn = predInsns.get(predInsns.size() - 1); + } - Rop constRop = Rops.opConst(cst); + if (insn.canThrow()) { + /* + * Don't move anything other than strings -- the risk + * of changing where an exception is thrown is too high. + */ + if (!(cst instanceof CstString) || !collectStrings) { + continue; + } + /* + * We can't move any throwable const whose throw will be + * caught, so don't count them. + */ + if (insn.getBlock().getSuccessors().cardinality() > 1) { + continue; + } + } + + /* + * TODO(dx team): Might be nice to try and figure out which local + * wins most when collected. + */ + if (ssaMeth.isRegALocal(result)) { + if (!COLLECT_ONE_LOCAL) { + continue; + } else { + if (usedByLocal.contains(cst)) { + // Count one local usage only. + continue; + } else { + usedByLocal.add(cst); + } + } + } + + Integer has = countUses.get(cst); + if (has == null) { + countUses.put(cst, 1); + } else { + countUses.put(cst, has + 1); + } + } - if (constRop.getBranchingness() == Rop.BRANCH_NONE) { - start.addInsnToHead( - new PlainCstInsn(Rops.opConst(cst), - SourcePosition.NO_INFO, result, - RegisterSpecList.EMPTY, cst)); - } else { - // We need two new basic blocks along with the new insn - SsaBasicBlock entryBlock = ssaMeth.getEntryBlock(); - SsaBasicBlock successorBlock - = entryBlock.getPrimarySuccessor(); + // Collect constants that have been reused. + ArrayList<TypedConstant> constantList = new ArrayList<TypedConstant>(); + for (Map.Entry<TypedConstant, Integer> entry : countUses.entrySet()) { + if (entry.getValue() > 1) { + constantList.add(entry.getKey()); + } + } - // Insert a block containing the const insn. - SsaBasicBlock constBlock - = entryBlock.insertNewSuccessor(successorBlock); + // Sort by use, with most used at the beginning of the list. + Collections.sort(constantList, new Comparator<Constant>() { + @Override + public int compare(Constant a, Constant b) { + int ret; + ret = countUses.get(b) - countUses.get(a); + + if (ret == 0) { + /* + * Provide sorting determinisim for constants with same + * usage count. + */ + ret = a.compareTo(b); + } - constBlock.replaceLastInsn( - new ThrowingCstInsn(constRop, SourcePosition.NO_INFO, - RegisterSpecList.EMPTY, - StdTypeList.EMPTY, cst)); + return ret; + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + }); + + return constantList; + } + + /** + * Inserts mark-locals if necessary when changing a register. If + * the definition of {@code origReg} is associated with a local + * variable, then insert a mark-local for {@code newReg} just below + * it. We expect the definition of {@code origReg} to ultimately + * be removed by the dead code eliminator + * + * @param origReg {@code non-null;} original register + * @param newReg {@code non-null;} new register that will replace + * {@code origReg} + */ + private void fixLocalAssignment(RegisterSpec origReg, RegisterSpec newReg) { + for (SsaInsn use : ssaMeth.getUseListForRegister(origReg.getReg())) { + RegisterSpec localAssignment = use.getLocalAssignment(); + if (localAssignment == null) { + continue; + } + + if (use.getResult() == null) { + /* + * This is a mark-local. it will be updated when all uses + * are updated. + */ + continue; + } - // Insert a block containing the move-result-pseudo insn. + LocalItem local = localAssignment.getLocalItem(); - SsaBasicBlock resultBlock - = constBlock.insertNewSuccessor(successorBlock); - PlainInsn insn - = new PlainInsn( - Rops.opMoveResultPseudo(result.getTypeBearer()), - SourcePosition.NO_INFO, - result, RegisterSpecList.EMPTY); + // Un-associate original use. + use.setResultLocal(null); - resultBlock.addInsnToHead(insn); - } + // Now add a mark-local to the new reg immediately after. + newReg = newReg.withLocalItem(local); - newRegs.put(cst, result); - } + SsaInsn newInsn = SsaInsn.makeFromRop(new PlainInsn(Rops.opMarkLocal(newReg), + SourcePosition.NO_INFO, null, RegisterSpecList.make(newReg)), use.getBlock()); - updateConstUses(newRegs, regSz); - } + ArrayList<SsaInsn> insns = use.getBlock().getInsns(); - /** - * Gets all of the collectable constant values used in this method, - * sorted by most used first. Skips non-collectable consts, such as - * non-string object constants - * - * @return {@code non-null;} list of constants in most-to-least used order + insns.add(insns.indexOf(use) + 1, newInsn); + } + } + + /** + * Updates all uses of various consts to use the values in the newly + * assigned registers. + * + * @param newRegs {@code non-null;} mapping between constant and new reg + * @param origRegCount {@code >=0;} original SSA reg count, not including + * newly added constant regs + */ + private void updateConstUses(HashMap<TypedConstant, RegisterSpec> newRegs, int origRegCount) { + + /* + * set of constants associated with a local variable; used + * only if COLLECT_ONE_LOCAL is true. */ - private ArrayList<TypedConstant> getConstsSortedByCountUse() { - int regSz = ssaMeth.getRegCount(); - - final HashMap<TypedConstant, Integer> countUses - = new HashMap<TypedConstant, Integer>(); - - /* - * Each collected constant can be used by just one local - * (used only if COLLECT_ONE_LOCAL is true). - */ - final HashSet<TypedConstant> usedByLocal - = new HashSet<TypedConstant>(); - - // Count how many times each const value is used. - for (int i = 0; i < regSz; i++) { - SsaInsn insn = ssaMeth.getDefinitionForRegister(i); - - if (insn == null || insn.getOpcode() == null) continue; - - RegisterSpec result = insn.getResult(); - TypeBearer typeBearer = result.getTypeBearer(); - - if (!typeBearer.isConstant()) continue; - - TypedConstant cst = (TypedConstant) typeBearer; - - // Find defining instruction for move-result-pseudo instructions - if (insn.getOpcode().getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { - int pred = insn.getBlock().getPredecessors().nextSetBit(0); - ArrayList<SsaInsn> predInsns; - predInsns = ssaMeth.getBlocks().get(pred).getInsns(); - insn = predInsns.get(predInsns.size()-1); - } - - if (insn.canThrow()) { - /* - * Don't move anything other than strings -- the risk - * of changing where an exception is thrown is too high. - */ - if (!(cst instanceof CstString) || !COLLECT_STRINGS) { - continue; - } - /* - * We can't move any throwable const whose throw will be - * caught, so don't count them. - */ - if (insn.getBlock().getSuccessors().cardinality() > 1) { - continue; - } - } - - /* - * TODO: Might be nice to try and figure out which local - * wins most when collected. - */ - if (ssaMeth.isRegALocal(result)) { - if (!COLLECT_ONE_LOCAL) { - continue; - } else { - if (usedByLocal.contains(cst)) { - // Count one local usage only. - continue; - } else { - usedByLocal.add(cst); - } - } - } - - Integer has = countUses.get(cst); - if (has == null) { - countUses.put(cst, 1); - } else { - countUses.put(cst, has + 1); - } + final HashSet<TypedConstant> usedByLocal = new HashSet<TypedConstant>(); + + final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy(); + + for (int i = 0; i < origRegCount; i++) { + SsaInsn insn = ssaMeth.getDefinitionForRegister(i); + + if (insn == null) { + continue; + } + + final RegisterSpec origReg = insn.getResult(); + TypeBearer typeBearer = insn.getResult().getTypeBearer(); + + if (!typeBearer.isConstant()) { + continue; + } + + TypedConstant cst = (TypedConstant) typeBearer; + final RegisterSpec newReg = newRegs.get(cst); + + if (newReg == null) { + continue; + } + + if (ssaMeth.isRegALocal(origReg)) { + if (!COLLECT_ONE_LOCAL) { + continue; + } else { + /* + * TODO(dx team): If the same local gets the same cst + * multiple times, it would be nice to reuse the + * register. + */ + if (usedByLocal.contains(cst)) { + continue; + } else { + usedByLocal.add(cst); + fixLocalAssignment(origReg, newRegs.get(cst)); + } } + } - // Collect constants that have been reused. - ArrayList<TypedConstant> constantList = new ArrayList<TypedConstant>(); - for (Map.Entry<TypedConstant, Integer> entry : countUses.entrySet()) { - if (entry.getValue() > 1) { - constantList.add(entry.getKey()); - } + // maps an original const register to the new collected register + RegisterMapper mapper = new RegisterMapper() { + @Override + public int getNewRegisterCount() { + return ssaMeth.getRegCount(); } - // Sort by use, with most used at the beginning of the list. - Collections.sort(constantList, new Comparator<Constant>() { - public int compare(Constant a, Constant b) { - int ret; - ret = countUses.get(b) - countUses.get(a); - - if (ret == 0) { - /* - * Provide sorting determinisim for constants with same - * usage count. - */ - ret = a.compareTo(b); - } - - return ret; - } - - @Override - public boolean equals (Object obj) { - return obj == this; - } - }); - - return constantList; - } + @Override + public RegisterSpec map(RegisterSpec registerSpec) { + if (registerSpec.getReg() == origReg.getReg()) { + return newReg.withLocalItem(registerSpec.getLocalItem()); + } - /** - * Inserts mark-locals if necessary when changing a register. If - * the definition of {@code origReg} is associated with a local - * variable, then insert a mark-local for {@code newReg} just below - * it. We expect the definition of {@code origReg} to ultimately - * be removed by the dead code eliminator - * - * @param origReg {@code non-null;} original register - * @param newReg {@code non-null;} new register that will replace - * {@code origReg} - */ - private void fixLocalAssignment(RegisterSpec origReg, - RegisterSpec newReg) { - for (SsaInsn use : ssaMeth.getUseListForRegister(origReg.getReg())) { - RegisterSpec localAssignment = use.getLocalAssignment(); - if (localAssignment == null) { - continue; - } - - if (use.getResult() == null) { - /* - * This is a mark-local. it will be updated when all uses - * are updated. - */ - continue; - } - - LocalItem local = localAssignment.getLocalItem(); - - // Un-associate original use. - use.setResultLocal(null); - - // Now add a mark-local to the new reg immediately after. - newReg = newReg.withLocalItem(local); - - SsaInsn newInsn - = SsaInsn.makeFromRop( - new PlainInsn(Rops.opMarkLocal(newReg), - SourcePosition.NO_INFO, null, - RegisterSpecList.make(newReg)), - use.getBlock()); - - ArrayList<SsaInsn> insns = use.getBlock().getInsns(); - - insns.add(insns.indexOf(use) + 1, newInsn); + return registerSpec; } - } + }; - /** - * Updates all uses of various consts to use the values in the newly - * assigned registers. - * - * @param newRegs {@code non-null;} mapping between constant and new reg - * @param origRegCount {@code >=0;} original SSA reg count, not including - * newly added constant regs - */ - private void updateConstUses(HashMap<TypedConstant, RegisterSpec> newRegs, - int origRegCount) { - - /* - * set of constants associated with a local variable; used - * only if COLLECT_ONE_LOCAL is true. - */ - final HashSet<TypedConstant> usedByLocal - = new HashSet<TypedConstant>(); - - final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy(); - - for (int i = 0; i < origRegCount; i++) { - SsaInsn insn = ssaMeth.getDefinitionForRegister(i); - - if (insn == null) { - continue; - } - - final RegisterSpec origReg = insn.getResult(); - TypeBearer typeBearer = insn.getResult().getTypeBearer(); - - if (!typeBearer.isConstant()) continue; - - TypedConstant cst = (TypedConstant) typeBearer; - final RegisterSpec newReg = newRegs.get(cst); - - if (newReg == null) { - continue; - } - - if (ssaMeth.isRegALocal(origReg)) { - if (!COLLECT_ONE_LOCAL) { - continue; - } else { - /* - * TODO: If the same local gets the same cst - * multiple times, it would be nice to reuse the - * register. - */ - if (usedByLocal.contains(cst)) { - continue; - } else { - usedByLocal.add(cst); - fixLocalAssignment(origReg, newRegs.get(cst)); - } - } - } - - // maps an original const register to the new collected register - RegisterMapper mapper = new RegisterMapper() { - @Override - public int getNewRegisterCount() { - return ssaMeth.getRegCount(); - } - - @Override - public RegisterSpec map(RegisterSpec registerSpec) { - if (registerSpec.getReg() == origReg.getReg()) { - return newReg.withLocalItem( - registerSpec.getLocalItem()); - } - - return registerSpec; - } - }; - - for (SsaInsn use : useList[origReg.getReg()]) { - if (use.canThrow() - && use.getBlock().getSuccessors().cardinality() > 1) { - continue; - } - use.mapSourceRegisters(mapper); - } + for (SsaInsn use : useList[origReg.getReg()]) { + if (use.canThrow() && use.getBlock().getSuccessors().cardinality() > 1) { + continue; } + use.mapSourceRegisters(mapper); + } } + } } diff --git a/dx/src/com/android/jack/dx/ssa/DeadCodeRemover.java b/dx/src/com/android/jack/dx/ssa/DeadCodeRemover.java index 70c0005..27040cf 100644 --- a/dx/src/com/android/jack/dx/ssa/DeadCodeRemover.java +++ b/dx/src/com/android/jack/dx/ssa/DeadCodeRemover.java @@ -16,14 +16,8 @@ package com.android.jack.dx.ssa; -import com.android.jack.dx.rop.code.Insn; -import com.android.jack.dx.rop.code.PlainInsn; -import com.android.jack.dx.rop.code.RegOps; import com.android.jack.dx.rop.code.RegisterSpec; import com.android.jack.dx.rop.code.RegisterSpecList; -import com.android.jack.dx.rop.code.Rop; -import com.android.jack.dx.rop.code.Rops; -import com.android.jack.dx.rop.code.SourcePosition; import java.util.ArrayList; import java.util.BitSet; @@ -32,241 +26,244 @@ import java.util.HashSet; /** * A variation on Appel Algorithm 19.12 "Dead code elimination in SSA form". * - * TODO this algorithm is more efficient if run in reverse from exit + * TODO(dx team) this algorithm is more efficient if run in reverse from exit * block to entry block. */ public class DeadCodeRemover { - /** method we're processing */ - private final SsaMethod ssaMeth; - - /** ssaMeth.getRegCount() */ - private final int regCount; + /** method we're processing */ + private final SsaMethod ssaMeth; + + /** ssaMeth.getRegCount() */ + private final int regCount; + + /** + * indexed by register: whether reg should be examined + * (does it correspond to a no-side-effect insn?) + */ + private final BitSet worklist; + + /** use list indexed by register; modified during operation */ + private final ArrayList<SsaInsn>[] useList; + + /** + * Process a method with the dead-code remver + * + * @param ssaMethod method to process + */ + public static void process(SsaMethod ssaMethod) { + DeadCodeRemover dc = new DeadCodeRemover(ssaMethod); + dc.run(); + } + + /** + * Constructs an instance. + * + * @param ssaMethod method to process + */ + private DeadCodeRemover(SsaMethod ssaMethod) { + this.ssaMeth = ssaMethod; + + regCount = ssaMethod.getRegCount(); + worklist = new BitSet(regCount); + useList = ssaMeth.getUseListCopy(); + } + + /** + * Runs the dead code remover. + */ + private void run() { + pruneDeadInstructions(); + + HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>(); + + ssaMeth.forEachInsn(new NoSideEffectVisitor(worklist)); + + int regV; + + while (0 <= (regV = worklist.nextSetBit(0))) { + worklist.clear(regV); + + if (useList[regV].size() == 0 || isCircularNoSideEffect(regV, null)) { + + SsaInsn insnS = ssaMeth.getDefinitionForRegister(regV); + + // This insn has already been deleted. + if (deletedInsns.contains(insnS)) { + continue; + } - /** - * indexed by register: whether reg should be examined - * (does it correspond to a no-side-effect insn?) - */ - private final BitSet worklist; + RegisterSpecList sources = insnS.getSources(); - /** use list indexed by register; modified during operation */ - private final ArrayList<SsaInsn>[] useList; + int sz = sources.size(); + for (int i = 0; i < sz; i++) { + // Delete this insn from all usage lists. + RegisterSpec source = sources.get(i); + useList[source.getReg()].remove(insnS); - /** - * Process a method with the dead-code remver - * - * @param ssaMethod method to process - */ - public static void process(SsaMethod ssaMethod) { - DeadCodeRemover dc = new DeadCodeRemover(ssaMethod); - dc.run(); - } - - /** - * Constructs an instance. - * - * @param ssaMethod method to process - */ - private DeadCodeRemover(SsaMethod ssaMethod) { - this.ssaMeth = ssaMethod; + if (!hasSideEffect(ssaMeth.getDefinitionForRegister(source.getReg()))) { + /* + * Only registers whose definition has no side effect + * should be added back to the worklist. + */ + worklist.set(source.getReg()); + } + } - regCount = ssaMethod.getRegCount(); - worklist = new BitSet(regCount); - useList = ssaMeth.getUseListCopy(); + // Schedule this insn for later deletion. + deletedInsns.add(insnS); + } } - /** - * Runs the dead code remover. - */ - private void run() { - pruneDeadInstructions(); - - HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>(); - - ssaMeth.forEachInsn(new NoSideEffectVisitor(worklist)); - - int regV; - - while ( 0 <= (regV = worklist.nextSetBit(0)) ) { - worklist.clear(regV); + ssaMeth.deleteInsns(deletedInsns); + } - if (useList[regV].size() == 0 - || isCircularNoSideEffect(regV, null)) { + /** + * Removes all instructions from every unreachable block. + */ + private void pruneDeadInstructions() { + HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>(); - SsaInsn insnS = ssaMeth.getDefinitionForRegister(regV); + ssaMeth.computeReachability(); - // This insn has already been deleted. - if (deletedInsns.contains(insnS)) { - continue; - } + for (SsaBasicBlock block : ssaMeth.getBlocks()) { + if (block.isReachable()) { + continue; + } - RegisterSpecList sources = insnS.getSources(); + // Prune instructions from unreachable blocks + for (int i = 0; i < block.getInsns().size(); i++) { + SsaInsn insn = block.getInsns().get(i); + RegisterSpecList sources = insn.getSources(); + int sourcesSize = sources.size(); - int sz = sources.size(); - for (int i = 0; i < sz; i++) { - // Delete this insn from all usage lists. - RegisterSpec source = sources.get(i); - useList[source.getReg()].remove(insnS); - - if (!hasSideEffect( - ssaMeth.getDefinitionForRegister( - source.getReg()))) { - /* - * Only registers whose definition has no side effect - * should be added back to the worklist. - */ - worklist.set(source.getReg()); - } - } + // Delete this instruction completely if it has sources + if (sourcesSize != 0) { + deletedInsns.add(insn); + } - // Schedule this insn for later deletion. - deletedInsns.add(insnS); - } + // Delete this instruction from all usage lists. + for (int j = 0; j < sourcesSize; j++) { + RegisterSpec source = sources.get(j); + useList[source.getReg()].remove(insn); } - ssaMeth.deleteInsns(deletedInsns); + // Remove this instruction result from the sources of any phis + RegisterSpec result = insn.getResult(); + if (result == null) { + continue; + } + for (SsaInsn use : useList[result.getReg()]) { + if (use instanceof PhiInsn) { + PhiInsn phiUse = (PhiInsn) use; + phiUse.removePhiRegister(result); + } + } + } } - /** - * Removes all instructions from every unreachable block. - */ - private void pruneDeadInstructions() { - HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>(); - - ssaMeth.computeReachability(); - - for (SsaBasicBlock block : ssaMeth.getBlocks()) { - if (block.isReachable()) continue; - - // Prune instructions from unreachable blocks - for (int i = 0; i < block.getInsns().size(); i++) { - SsaInsn insn = block.getInsns().get(i); - RegisterSpecList sources = insn.getSources(); - int sourcesSize = sources.size(); - - // Delete this instruction completely if it has sources - if (sourcesSize != 0) { - deletedInsns.add(insn); - } - - // Delete this instruction from all usage lists. - for (int j = 0; j < sourcesSize; j++) { - RegisterSpec source = sources.get(j); - useList[source.getReg()].remove(insn); - } - - // Remove this instruction result from the sources of any phis - RegisterSpec result = insn.getResult(); - if (result == null) continue; - for (SsaInsn use : useList[result.getReg()]) { - if (use instanceof PhiInsn) { - PhiInsn phiUse = (PhiInsn) use; - phiUse.removePhiRegister(result); - } - } - } - } + ssaMeth.deleteInsns(deletedInsns); + } + + /** + * Returns true if the only uses of this register form a circle of + * operations with no side effects. + * + * @param regV register to examine + * @param set a set of registers that we've already determined + * are only used as sources in operations with no side effect or null + * if this is the first recursion + * @return true if usage is circular without side effect + */ + private boolean isCircularNoSideEffect(int regV, BitSet set) { + if ((set != null) && set.get(regV)) { + return true; + } - ssaMeth.deleteInsns(deletedInsns); + for (SsaInsn use : useList[regV]) { + if (hasSideEffect(use)) { + return false; + } } - /** - * Returns true if the only uses of this register form a circle of - * operations with no side effects. - * - * @param regV register to examine - * @param set a set of registers that we've already determined - * are only used as sources in operations with no side effect or null - * if this is the first recursion - * @return true if usage is circular without side effect - */ - private boolean isCircularNoSideEffect(int regV, BitSet set) { - if ((set != null) && set.get(regV)) { - return true; - } + if (set == null) { + set = new BitSet(regCount); + } - for (SsaInsn use : useList[regV]) { - if (hasSideEffect(use)) { - return false; - } - } + // This register is only used in operations that have no side effect. + set.set(regV); - if (set == null) { - set = new BitSet(regCount); - } + for (SsaInsn use : useList[regV]) { + RegisterSpec result = use.getResult(); - // This register is only used in operations that have no side effect. - set.set(regV); + if (result == null || !isCircularNoSideEffect(result.getReg(), set)) { + return false; + } + } - for (SsaInsn use : useList[regV]) { - RegisterSpec result = use.getResult(); + return true; + } + + /** + * Returns true if this insn has a side-effect. Returns true + * if the insn is null for reasons stated in the code block. + * + * @param insn {@code null-ok;} instruction in question + * @return true if it has a side-effect + */ + private static boolean hasSideEffect(SsaInsn insn) { + if (insn == null) { + /* While false would seem to make more sense here, true + * prevents us from adding this back to a worklist unnecessarally. + */ + return true; + } - if (result == null - || !isCircularNoSideEffect(result.getReg(), set)) { - return false; - } - } + return insn.hasSideEffect(); + } - return true; - } + /** + * A callback class used to build up the initial worklist of + * registers defined by an instruction with no side effect. + */ + private static class NoSideEffectVisitor implements SsaInsn.Visitor { + BitSet noSideEffectRegs; /** - * Returns true if this insn has a side-effect. Returns true - * if the insn is null for reasons stated in the code block. + * Passes in data structures that will be filled out after + * ssaMeth.forEachInsn() is called with this instance. * - * @param insn {@code null-ok;} instruction in question - * @return true if it has a side-effect + * @param noSideEffectRegs to-build bitset of regs that are + * results of regs with no side effects */ - private static boolean hasSideEffect(SsaInsn insn) { - if (insn == null) { - /* While false would seem to make more sense here, true - * prevents us from adding this back to a worklist unnecessarally. - */ - return true; - } - - return insn.hasSideEffect(); + public NoSideEffectVisitor(BitSet noSideEffectRegs) { + this.noSideEffectRegs = noSideEffectRegs; } - /** - * A callback class used to build up the initial worklist of - * registers defined by an instruction with no side effect. - */ - static private class NoSideEffectVisitor implements SsaInsn.Visitor { - BitSet noSideEffectRegs; - - /** - * Passes in data structures that will be filled out after - * ssaMeth.forEachInsn() is called with this instance. - * - * @param noSideEffectRegs to-build bitset of regs that are - * results of regs with no side effects - */ - public NoSideEffectVisitor(BitSet noSideEffectRegs) { - this.noSideEffectRegs = noSideEffectRegs; - } - - /** {@inheritDoc} */ - public void visitMoveInsn (NormalSsaInsn insn) { - // If we're tracking local vars, some moves have side effects. - if (!hasSideEffect(insn)) { - noSideEffectRegs.set(insn.getResult().getReg()); - } - } + /** {@inheritDoc} */ + @Override + public void visitMoveInsn(NormalSsaInsn insn) { + // If we're tracking local vars, some moves have side effects. + if (!hasSideEffect(insn)) { + noSideEffectRegs.set(insn.getResult().getReg()); + } + } - /** {@inheritDoc} */ - public void visitPhiInsn (PhiInsn phi) { - // If we're tracking local vars, then some phis have side effects. - if (!hasSideEffect(phi)) { - noSideEffectRegs.set(phi.getResult().getReg()); - } - } + /** {@inheritDoc} */ + @Override + public void visitPhiInsn(PhiInsn phi) { + // If we're tracking local vars, then some phis have side effects. + if (!hasSideEffect(phi)) { + noSideEffectRegs.set(phi.getResult().getReg()); + } + } - /** {@inheritDoc} */ - public void visitNonMoveInsn (NormalSsaInsn insn) { - RegisterSpec result = insn.getResult(); - if (!hasSideEffect(insn) && result != null) { - noSideEffectRegs.set(result.getReg()); - } - } + /** {@inheritDoc} */ + @Override + public void visitNonMoveInsn(NormalSsaInsn insn) { + RegisterSpec result = insn.getResult(); + if (!hasSideEffect(insn) && result != null) { + noSideEffectRegs.set(result.getReg()); + } } + } } diff --git a/dx/src/com/android/jack/dx/ssa/DomFront.java b/dx/src/com/android/jack/dx/ssa/DomFront.java index 9e67f46..5a599ff 100644 --- a/dx/src/com/android/jack/dx/ssa/DomFront.java +++ b/dx/src/com/android/jack/dx/ssa/DomFront.java @@ -16,12 +16,9 @@ package com.android.jack.dx.ssa; -import com.android.jack.dx.util.BitIntSet; import com.android.jack.dx.util.IntSet; -import com.android.jack.dx.util.ListIntSet; import java.util.ArrayList; -import java.util.Arrays; import java.util.BitSet; /** @@ -30,175 +27,172 @@ import java.util.BitSet; * Harvey, and Kennedy; transliterated to Java. */ public class DomFront { - /** local debug flag */ - private static boolean DEBUG = false; + /** local debug flag */ + private static final boolean DEBUG = false; - /** {@code non-null;} method being processed */ - private final SsaMethod meth; + /** {@code non-null;} method being processed */ + private final SsaMethod meth; - private final ArrayList<SsaBasicBlock> nodes; + private final ArrayList<SsaBasicBlock> nodes; - private final DomInfo[] domInfos; + private final DomInfo[] domInfos; + /** + * Dominance-frontier information for a single basic block. + */ + public static class DomInfo { /** - * Dominance-frontier information for a single basic block. + * {@code null-ok;} the dominance frontier set indexed by + * block index */ - public static class DomInfo { - /** - * {@code null-ok;} the dominance frontier set indexed by - * block index - */ - public IntSet dominanceFrontiers; - - /** {@code >= 0 after run();} the index of the immediate dominator */ - public int idom = -1; + public IntSet dominanceFrontiers; + + /** {@code >= 0 after run();} the index of the immediate dominator */ + public int idom = -1; + } + + /** + * Constructs instance. Call {@link DomFront#run} to process. + * + * @param meth {@code non-null;} method to process + */ + public DomFront(SsaMethod meth) { + this.meth = meth; + nodes = meth.getBlocks(); + + int szNodes = nodes.size(); + domInfos = new DomInfo[szNodes]; + + for (int i = 0; i < szNodes; i++) { + domInfos[i] = new DomInfo(); + } + } + + /** + * Calculates the dominance frontier information for the method. + * + * @return {@code non-null;} an array of DomInfo structures + */ + public DomInfo[] run() { + int szNodes = nodes.size(); + + if (DEBUG) { + for (int i = 0; i < szNodes; i++) { + SsaBasicBlock node = nodes.get(i); + System.out.println("pred[" + i + "]: " + node.getPredecessors()); + } } - /** - * Constructs instance. Call {@link DomFront#run} to process. - * - * @param meth {@code non-null;} method to process - */ - public DomFront(SsaMethod meth) { - this.meth = meth; - nodes = meth.getBlocks(); - - int szNodes = nodes.size(); - domInfos = new DomInfo[szNodes]; + Dominators.make(meth, domInfos, false); - for (int i = 0; i < szNodes; i++) { - domInfos[i] = new DomInfo(); - } + if (DEBUG) { + for (int i = 0; i < szNodes; i++) { + DomInfo info = domInfos[i]; + System.out.println("idom[" + i + "]: " + info.idom); + } } - /** - * Calculates the dominance frontier information for the method. - * - * @return {@code non-null;} an array of DomInfo structures - */ - public DomInfo[] run() { - int szNodes = nodes.size(); - - if (DEBUG) { - for (int i = 0; i < szNodes; i++) { - SsaBasicBlock node = nodes.get(i); - System.out.println("pred[" + i + "]: " - + node.getPredecessors()); - } - } - - Dominators methDom = Dominators.make(meth, domInfos, false); + buildDomTree(); - if (DEBUG) { - for (int i = 0; i < szNodes; i++) { - DomInfo info = domInfos[i]; - System.out.println("idom[" + i + "]: " - + info.idom); - } - } + if (DEBUG) { + debugPrintDomChildren(); + } - buildDomTree(); + for (int i = 0; i < szNodes; i++) { + domInfos[i].dominanceFrontiers = SetFactory.makeDomFrontSet(szNodes); + } - if (DEBUG) { - debugPrintDomChildren(); - } + calcDomFronts(); - for (int i = 0; i < szNodes; i++) { - domInfos[i].dominanceFrontiers - = SetFactory.makeDomFrontSet(szNodes); - } - - calcDomFronts(); + if (DEBUG) { + for (int i = 0; i < szNodes; i++) { + System.out.println("df[" + i + "]: " + domInfos[i].dominanceFrontiers); + } + } - if (DEBUG) { - for (int i = 0; i < szNodes; i++) { - System.out.println("df[" + i + "]: " - + domInfos[i].dominanceFrontiers); - } - } + return domInfos; + } - return domInfos; - } + private void debugPrintDomChildren() { + int szNodes = nodes.size(); - private void debugPrintDomChildren() { - int szNodes = nodes.size(); - - for (int i = 0; i < szNodes; i++) { - SsaBasicBlock node = nodes.get(i); - StringBuffer sb = new StringBuffer(); - - sb.append('{'); - boolean comma = false; - for (SsaBasicBlock child : node.getDomChildren()) { - if (comma) { - sb.append(','); - } - sb.append(child); - comma = true; - } - sb.append('}'); + for (int i = 0; i < szNodes; i++) { + SsaBasicBlock node = nodes.get(i); + StringBuffer sb = new StringBuffer(); - System.out.println("domChildren[" + node + "]: " - + sb); + sb.append('{'); + boolean comma = false; + for (SsaBasicBlock child : node.getDomChildren()) { + if (comma) { + sb.append(','); } + sb.append(child); + comma = true; + } + sb.append('}'); + + System.out.println("domChildren[" + node + "]: " + sb); } + } - /** - * The dominators algorithm leaves us knowing who the immediate dominator - * is for each node. This sweeps the node list and builds the proper - * dominance tree. - */ - private void buildDomTree() { - int szNodes = nodes.size(); + /** + * The dominators algorithm leaves us knowing who the immediate dominator + * is for each node. This sweeps the node list and builds the proper + * dominance tree. + */ + private void buildDomTree() { + int szNodes = nodes.size(); - for (int i = 0; i < szNodes; i++) { - DomInfo info = domInfos[i]; + for (int i = 0; i < szNodes; i++) { + DomInfo info = domInfos[i]; - if (info.idom == -1) continue; + if (info.idom == -1) { + continue; + } - SsaBasicBlock domParent = nodes.get(info.idom); - domParent.addDomChild(nodes.get(i)); - } + SsaBasicBlock domParent = nodes.get(info.idom); + domParent.addDomChild(nodes.get(i)); } + } + + /** + * Calculates the dominance-frontier set. + * from "A Simple, Fast Dominance Algorithm" by Cooper, + * Harvey, and Kennedy; transliterated to Java. + */ + private void calcDomFronts() { + int szNodes = nodes.size(); + + for (int b = 0; b < szNodes; b++) { + SsaBasicBlock nb = nodes.get(b); + DomInfo nbInfo = domInfos[b]; + BitSet pred = nb.getPredecessors(); + + if (pred.cardinality() > 1) { + for (int i = pred.nextSetBit(0); i >= 0; i = pred.nextSetBit(i + 1)) { + + for (int runnerIndex = i; runnerIndex != nbInfo.idom; /* empty */) { + /* + * We can stop if we hit a block we already + * added label to, since we must be at a part + * of the dom tree we have seen before. + */ + if (runnerIndex == -1) { + break; + } - /** - * Calculates the dominance-frontier set. - * from "A Simple, Fast Dominance Algorithm" by Cooper, - * Harvey, and Kennedy; transliterated to Java. - */ - private void calcDomFronts() { - int szNodes = nodes.size(); - - for (int b = 0; b < szNodes; b++) { - SsaBasicBlock nb = nodes.get(b); - DomInfo nbInfo = domInfos[b]; - BitSet pred = nb.getPredecessors(); - - if (pred.cardinality() > 1) { - for (int i = pred.nextSetBit(0); i >= 0; - i = pred.nextSetBit(i + 1)) { - - for (int runnerIndex = i; - runnerIndex != nbInfo.idom; /* empty */) { - /* - * We can stop if we hit a block we already - * added label to, since we must be at a part - * of the dom tree we have seen before. - */ - if (runnerIndex == -1) break; - - DomInfo runnerInfo = domInfos[runnerIndex]; - - if (runnerInfo.dominanceFrontiers.has(b)) { - break; - } - - // Add b to runner's dominance frontier set. - runnerInfo.dominanceFrontiers.add(b); - runnerIndex = runnerInfo.idom; - } - } + DomInfo runnerInfo = domInfos[runnerIndex]; + + if (runnerInfo.dominanceFrontiers.has(b)) { + break; } + + // Add b to runner's dominance frontier set. + runnerInfo.dominanceFrontiers.add(b); + runnerIndex = runnerInfo.idom; + } } + } } + } } diff --git a/dx/src/com/android/jack/dx/ssa/Dominators.java b/dx/src/com/android/jack/dx/ssa/Dominators.java index b3b4c3f..3311efd 100644 --- a/dx/src/com/android/jack/dx/ssa/Dominators.java +++ b/dx/src/com/android/jack/dx/ssa/Dominators.java @@ -41,245 +41,229 @@ import java.util.HashSet; * rank to keep the union-find tree balanced. */ public final class Dominators { - /* postdom is true if we want post dominators */ - private final boolean postdom; - - /* {@code non-null;} method being processed */ - private final SsaMethod meth; - - /* Method's basic blocks. */ - private final ArrayList<SsaBasicBlock> blocks; - - /** indexed by basic block index */ - private final DFSInfo[] info; - - private final ArrayList<SsaBasicBlock> vertex; - - /** {@code non-null;} the raw dominator info */ - private final DomFront.DomInfo domInfos[]; - - /** - * Constructs an instance. - * - * @param meth {@code non-null;} method to process - * @param domInfos {@code non-null;} the raw dominator info - * @param postdom true for postdom information, false for normal dom info - */ - private Dominators(SsaMethod meth, DomFront.DomInfo[] domInfos, - boolean postdom) { - this.meth = meth; - this.domInfos = domInfos; - this.postdom = postdom; - this.blocks = meth.getBlocks(); - this.info = new DFSInfo[blocks.size() + 2]; - this.vertex = new ArrayList<SsaBasicBlock>(); - } - - /** - * Constructs a fully-initialized instance. (This method exists so as - * to avoid calling a large amount of code in the constructor.) - * - * @param meth {@code non-null;} method to process - * @param domInfos {@code non-null;} the raw dominator info - * @param postdom true for postdom information, false for normal dom info - */ - public static Dominators make(SsaMethod meth, DomFront.DomInfo[] domInfos, - boolean postdom) { - Dominators result = new Dominators(meth, domInfos, postdom); - - result.run(); - return result; + /* postdom is true if we want post dominators */ + private final boolean postdom; + + /* {@code non-null;} method being processed */ + private final SsaMethod meth; + + /* Method's basic blocks. */ + private final ArrayList<SsaBasicBlock> blocks; + + /** indexed by basic block index */ + private final DFSInfo[] info; + + private final ArrayList<SsaBasicBlock> vertex; + + /** {@code non-null;} the raw dominator info */ + private final DomFront.DomInfo domInfos[]; + + /** + * Constructs an instance. + * + * @param meth {@code non-null;} method to process + * @param domInfos {@code non-null;} the raw dominator info + * @param postdom true for postdom information, false for normal dom info + */ + private Dominators(SsaMethod meth, DomFront.DomInfo[] domInfos, boolean postdom) { + this.meth = meth; + this.domInfos = domInfos; + this.postdom = postdom; + this.blocks = meth.getBlocks(); + this.info = new DFSInfo[blocks.size() + 2]; + this.vertex = new ArrayList<SsaBasicBlock>(); + } + + /** + * Constructs a fully-initialized instance. (This method exists so as + * to avoid calling a large amount of code in the constructor.) + * + * @param meth {@code non-null;} method to process + * @param domInfos {@code non-null;} the raw dominator info + * @param postdom true for postdom information, false for normal dom info + */ + public static Dominators make(SsaMethod meth, DomFront.DomInfo[] domInfos, boolean postdom) { + Dominators result = new Dominators(meth, domInfos, postdom); + + result.run(); + return result; + } + + private BitSet getPreds(SsaBasicBlock block) { + if (postdom) { + return block.getSuccessors(); + } else { + return block.getPredecessors(); } - - private BitSet getSuccs(SsaBasicBlock block) { - if (postdom) { - return block.getPredecessors(); - } else { - return block.getSuccessors(); + } + + /** + * Performs path compress on the DFS info. + * + * @param in Basic block whose DFS info we are path compressing. + */ + private void compress(SsaBasicBlock in) { + DFSInfo bbInfo = info[in.getIndex()]; + DFSInfo ancestorbbInfo = info[bbInfo.ancestor.getIndex()]; + + if (ancestorbbInfo.ancestor != null) { + ArrayList<SsaBasicBlock> worklist = new ArrayList<SsaBasicBlock>(); + HashSet<SsaBasicBlock> visited = new HashSet<SsaBasicBlock>(); + worklist.add(in); + + while (!worklist.isEmpty()) { + int wsize = worklist.size(); + SsaBasicBlock v = worklist.get(wsize - 1); + DFSInfo vbbInfo = info[v.getIndex()]; + SsaBasicBlock vAncestor = vbbInfo.ancestor; + DFSInfo vabbInfo = info[vAncestor.getIndex()]; + + // Make sure we process our ancestor before ourselves. + if (visited.add(vAncestor) && vabbInfo.ancestor != null) { + worklist.add(vAncestor); + continue; } - } + worklist.remove(wsize - 1); - private BitSet getPreds(SsaBasicBlock block) { - if (postdom) { - return block.getSuccessors(); - } else { - return block.getPredecessors(); + // Update based on ancestor info. + if (vabbInfo.ancestor == null) { + continue; } - } - - /** - * Performs path compress on the DFS info. - * - * @param in Basic block whose DFS info we are path compressing. - */ - private void compress(SsaBasicBlock in) { - DFSInfo bbInfo = info[in.getIndex()]; - DFSInfo ancestorbbInfo = info[bbInfo.ancestor.getIndex()]; - - if (ancestorbbInfo.ancestor != null) { - ArrayList<SsaBasicBlock> worklist = new ArrayList<SsaBasicBlock>(); - HashSet<SsaBasicBlock> visited = new HashSet<SsaBasicBlock>(); - worklist.add(in); - - while (!worklist.isEmpty()) { - int wsize = worklist.size(); - SsaBasicBlock v = worklist.get(wsize - 1); - DFSInfo vbbInfo = info[v.getIndex()]; - SsaBasicBlock vAncestor = vbbInfo.ancestor; - DFSInfo vabbInfo = info[vAncestor.getIndex()]; - - // Make sure we process our ancestor before ourselves. - if (visited.add(vAncestor) && vabbInfo.ancestor != null) { - worklist.add(vAncestor); - continue; - } - worklist.remove(wsize - 1); - - // Update based on ancestor info. - if (vabbInfo.ancestor == null) { - continue; - } - SsaBasicBlock vAncestorRep = vabbInfo.rep; - SsaBasicBlock vRep = vbbInfo.rep; - if (info[vAncestorRep.getIndex()].semidom - < info[vRep.getIndex()].semidom) { - vbbInfo.rep = vAncestorRep; - } - vbbInfo.ancestor = vabbInfo.ancestor; - } + SsaBasicBlock vAncestorRep = vabbInfo.rep; + SsaBasicBlock vRep = vbbInfo.rep; + if (info[vAncestorRep.getIndex()].semidom < info[vRep.getIndex()].semidom) { + vbbInfo.rep = vAncestorRep; } + vbbInfo.ancestor = vabbInfo.ancestor; + } } + } - private SsaBasicBlock eval(SsaBasicBlock v) { - DFSInfo bbInfo = info[v.getIndex()]; + private SsaBasicBlock eval(SsaBasicBlock v) { + DFSInfo bbInfo = info[v.getIndex()]; - if (bbInfo.ancestor == null) { - return v; - } + if (bbInfo.ancestor == null) { + return v; + } - compress(v); - return bbInfo.rep; + compress(v); + return bbInfo.rep; + } + + /** + * Performs dominator/post-dominator calculation for the control + * flow graph. + * + * @param meth {@code non-null;} method to analyze + */ + private void run() { + SsaBasicBlock root = postdom ? meth.getExitBlock() : meth.getEntryBlock(); + + if (root != null) { + vertex.add(root); + domInfos[root.getIndex()].idom = root.getIndex(); } - /** - * Performs dominator/post-dominator calculation for the control - * flow graph. - * - * @param meth {@code non-null;} method to analyze + /* + * First we perform a DFS numbering of the blocks, by + * numbering the dfs tree roots. */ - private void run() { - SsaBasicBlock root = postdom - ? meth.getExitBlock() : meth.getEntryBlock(); - if (root != null) { - vertex.add(root); - domInfos[root.getIndex()].idom = root.getIndex(); - } +DfsWalker walker = new DfsWalker(); + meth.forEachBlockDepthFirst(postdom, walker); + + // the largest semidom number assigned + int dfsMax = vertex.size() - 1; + + // Now calculate semidominators. + for (int i = dfsMax; i >= 2; --i) { + SsaBasicBlock w = vertex.get(i); + DFSInfo wInfo = info[w.getIndex()]; + + BitSet preds = getPreds(w); + for (int j = preds.nextSetBit(0); j >= 0; j = preds.nextSetBit(j + 1)) { + SsaBasicBlock predBlock = blocks.get(j); + DFSInfo predInfo = info[predBlock.getIndex()]; /* - * First we perform a DFS numbering of the blocks, by - * numbering the dfs tree roots. + * PredInfo may not exist in case the predecessor is + * not reachable. */ - - DfsWalker walker = new DfsWalker(); - meth.forEachBlockDepthFirst(postdom, walker); - - // the largest semidom number assigned - int dfsMax = vertex.size() - 1; - - // Now calculate semidominators. - for (int i = dfsMax; i >= 2; --i) { - SsaBasicBlock w = vertex.get(i); - DFSInfo wInfo = info[w.getIndex()]; - - BitSet preds = getPreds(w); - for (int j = preds.nextSetBit(0); - j >= 0; - j = preds.nextSetBit(j + 1)) { - SsaBasicBlock predBlock = blocks.get(j); - DFSInfo predInfo = info[predBlock.getIndex()]; - - /* - * PredInfo may not exist in case the predecessor is - * not reachable. - */ - if (predInfo != null) { - int predSemidom = info[eval(predBlock).getIndex()].semidom; - if (predSemidom < wInfo.semidom) { - wInfo.semidom = predSemidom; - } - } - } - info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w); - - /* - * Normally we would call link here, but in our O(m log n) - * implementation this is equivalent to the following - * single line. - */ - wInfo.ancestor = wInfo.parent; - - // Implicity define idom for each vertex. - ArrayList<SsaBasicBlock> wParentBucket; - wParentBucket = info[wInfo.parent.getIndex()].bucket; - - while (!wParentBucket.isEmpty()) { - int lastItem = wParentBucket.size() - 1; - SsaBasicBlock last = wParentBucket.remove(lastItem); - SsaBasicBlock U = eval(last); - if (info[U.getIndex()].semidom - < info[last.getIndex()].semidom) { - domInfos[last.getIndex()].idom = U.getIndex(); - } else { - domInfos[last.getIndex()].idom = wInfo.parent.getIndex(); - } - } + if (predInfo != null) { + int predSemidom = info[eval(predBlock).getIndex()].semidom; + if (predSemidom < wInfo.semidom) { + wInfo.semidom = predSemidom; + } } - - // Now explicitly define the immediate dominator of each vertex - for (int i = 2; i <= dfsMax; ++i) { - SsaBasicBlock w = vertex.get(i); - if (domInfos[w.getIndex()].idom - != vertex.get(info[w.getIndex()].semidom).getIndex()) { - domInfos[w.getIndex()].idom - = domInfos[domInfos[w.getIndex()].idom].idom; - } + } + info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w); + + /* + * Normally we would call link here, but in our O(m log n) + * implementation this is equivalent to the following + * single line. + */ + wInfo.ancestor = wInfo.parent; + + // Implicity define idom for each vertex. + ArrayList<SsaBasicBlock> wParentBucket; + wParentBucket = info[wInfo.parent.getIndex()].bucket; + + while (!wParentBucket.isEmpty()) { + int lastItem = wParentBucket.size() - 1; + SsaBasicBlock last = wParentBucket.remove(lastItem); + SsaBasicBlock u = eval(last); + if (info[u.getIndex()].semidom < info[last.getIndex()].semidom) { + domInfos[last.getIndex()].idom = u.getIndex(); + } else { + domInfos[last.getIndex()].idom = wInfo.parent.getIndex(); } + } } - /** - * Callback for depth-first walk through control flow graph (either - * from the entry block or the exit block). Records the traversal order - * in the {@code info}list. - */ - private class DfsWalker implements SsaBasicBlock.Visitor { - private int dfsNum = 0; - - public void visitBlock(SsaBasicBlock v, SsaBasicBlock parent) { - DFSInfo bbInfo = new DFSInfo(); - bbInfo.semidom = ++dfsNum; - bbInfo.rep = v; - bbInfo.parent = parent; - vertex.add(v); - info[v.getIndex()] = bbInfo; - } + // Now explicitly define the immediate dominator of each vertex + for (int i = 2; i <= dfsMax; ++i) { + SsaBasicBlock w = vertex.get(i); + if (domInfos[w.getIndex()].idom != vertex.get(info[w.getIndex()].semidom).getIndex()) { + domInfos[w.getIndex()].idom = domInfos[domInfos[w.getIndex()].idom].idom; + } + } + } + + /** + * Callback for depth-first walk through control flow graph (either + * from the entry block or the exit block). Records the traversal order + * in the {@code info}list. + */ + private class DfsWalker implements SsaBasicBlock.Visitor { + private int dfsNum = 0; + + @Override + public void visitBlock(SsaBasicBlock v, SsaBasicBlock parent) { + DFSInfo bbInfo = new DFSInfo(); + bbInfo.semidom = ++dfsNum; + bbInfo.rep = v; + bbInfo.parent = parent; + vertex.add(v); + info[v.getIndex()] = bbInfo; } + } - private static final class DFSInfo { - public int semidom; - public SsaBasicBlock parent; + private static final class DFSInfo { + public int semidom; + public SsaBasicBlock parent; - /** - * rep(resentative) is known as "label" in the paper. It is the node - * that our block's DFS info has been unioned to. - */ - public SsaBasicBlock rep; + /** + * rep(resentative) is known as "label" in the paper. It is the node + * that our block's DFS info has been unioned to. + */ + public SsaBasicBlock rep; - public SsaBasicBlock ancestor; - public ArrayList<SsaBasicBlock> bucket; + public SsaBasicBlock ancestor; + public ArrayList<SsaBasicBlock> bucket; - public DFSInfo() { - bucket = new ArrayList<SsaBasicBlock>(); - } + public DFSInfo() { + bucket = new ArrayList<SsaBasicBlock>(); } + } } diff --git a/dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java b/dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java index 791a3b5..6021941 100644 --- a/dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java +++ b/dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java @@ -50,794 +50,768 @@ import java.util.List; * the method they are created in and replaces the array values with registers. */ public class EscapeAnalysis { - /** - * Struct used to generate and maintain escape analysis results. - */ - static class EscapeSet { - /** set containing all registers related to an object */ - BitSet regSet; - /** escape state of the object */ - EscapeState escape; - /** list of objects that are put into this object */ - ArrayList<EscapeSet> childSets; - /** list of objects that this object is put into */ - ArrayList<EscapeSet> parentSets; - /** flag to indicate this object is a scalar replaceable array */ - boolean replaceableArray; - - /** - * Constructs an instance of an EscapeSet - * - * @param reg the SSA register that defines the object - * @param size the number of registers in the method - * @param escState the lattice value to initially set this to - */ - EscapeSet(int reg, int size, EscapeState escState) { - regSet = new BitSet(size); - regSet.set(reg); - escape = escState; - childSets = new ArrayList<EscapeSet>(); - parentSets = new ArrayList<EscapeSet>(); - replaceableArray = false; - } - } + /** + * Struct used to generate and maintain escape analysis results. + */ + static class EscapeSet { + /** set containing all registers related to an object */ + BitSet regSet; + /** escape state of the object */ + EscapeState escape; + /** list of objects that are put into this object */ + ArrayList<EscapeSet> childSets; + /** list of objects that this object is put into */ + ArrayList<EscapeSet> parentSets; + /** flag to indicate this object is a scalar replaceable array */ + boolean replaceableArray; /** - * Lattice values used to indicate escape state for an object. Analysis can - * only raise escape state values, not lower them. + * Constructs an instance of an EscapeSet * - * TOP - Used for objects that haven't been analyzed yet - * NONE - Object does not escape, and is eligible for scalar replacement. - * METHOD - Object remains local to method, but can't be scalar replaced. - * INTER - Object is passed between methods. (treated as globally escaping - * since this is an intraprocedural analysis) - * GLOBAL - Object escapes globally. + * @param reg the SSA register that defines the object + * @param size the number of registers in the method + * @param escState the lattice value to initially set this to */ - public enum EscapeState { - TOP, NONE, METHOD, INTER, GLOBAL + EscapeSet(int reg, int size, EscapeState escState) { + regSet = new BitSet(size); + regSet.set(reg); + escape = escState; + childSets = new ArrayList<EscapeSet>(); + parentSets = new ArrayList<EscapeSet>(); + replaceableArray = false; } - - /** method we're processing */ - private SsaMethod ssaMeth; - /** ssaMeth.getRegCount() */ - private int regCount; - /** Lattice values for each object register group */ - private ArrayList<EscapeSet> latticeValues; - - /** - * Constructs an instance. - * - * @param ssaMeth method to process - */ - private EscapeAnalysis(SsaMethod ssaMeth) { - this.ssaMeth = ssaMeth; - this.regCount = ssaMeth.getRegCount(); - this.latticeValues = new ArrayList<EscapeSet>(); - } - - /** - * Finds the index in the lattice for a particular register. - * Returns the size of the lattice if the register wasn't found. - * - * @param reg {@code non-null;} register being looked up - * @return index of the register or size of the lattice if it wasn't found. - */ - private int findSetIndex(RegisterSpec reg) { - int i; - for (i = 0; i < latticeValues.size(); i++) { - EscapeSet e = latticeValues.get(i); - if (e.regSet.get(reg.getReg())) { - return i; - } - } + } + + /** + * Lattice values used to indicate escape state for an object. Analysis can + * only raise escape state values, not lower them. + * + * TOP - Used for objects that haven't been analyzed yet + * NONE - Object does not escape, and is eligible for scalar replacement. + * METHOD - Object remains local to method, but can't be scalar replaced. + * INTER - Object is passed between methods. (treated as globally escaping + * since this is an intraprocedural analysis) + * GLOBAL - Object escapes globally. + */ + public enum EscapeState { + TOP, NONE, METHOD, INTER, GLOBAL + } + + /** method we're processing */ + private SsaMethod ssaMeth; + /** ssaMeth.getRegCount() */ + private int regCount; + /** Lattice values for each object register group */ + private ArrayList<EscapeSet> latticeValues; + + /** + * Constructs an instance. + * + * @param ssaMeth method to process + */ + private EscapeAnalysis(SsaMethod ssaMeth) { + this.ssaMeth = ssaMeth; + this.regCount = ssaMeth.getRegCount(); + this.latticeValues = new ArrayList<EscapeSet>(); + } + + /** + * Finds the index in the lattice for a particular register. + * Returns the size of the lattice if the register wasn't found. + * + * @param reg {@code non-null;} register being looked up + * @return index of the register or size of the lattice if it wasn't found. + */ + private int findSetIndex(RegisterSpec reg) { + int i; + for (i = 0; i < latticeValues.size(); i++) { + EscapeSet e = latticeValues.get(i); + if (e.regSet.get(reg.getReg())) { return i; + } } - - /** - * Finds the corresponding instruction for a given move result - * - * @param moveInsn {@code non-null;} a move result instruction - * @return {@code non-null;} the instruction that produces the result for - * the move - */ - private SsaInsn getInsnForMove(SsaInsn moveInsn) { - int pred = moveInsn.getBlock().getPredecessors().nextSetBit(0); - ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns(); - return predInsns.get(predInsns.size()-1); + return i; + } + + /** + * Finds the corresponding instruction for a given move result + * + * @param moveInsn {@code non-null;} a move result instruction + * @return {@code non-null;} the instruction that produces the result for + * the move + */ + private SsaInsn getInsnForMove(SsaInsn moveInsn) { + int pred = moveInsn.getBlock().getPredecessors().nextSetBit(0); + ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns(); + return predInsns.get(predInsns.size() - 1); + } + + /** + * Finds the corresponding move result for a given instruction + * + * @param insn {@code non-null;} an instruction that must always be + * followed by a move result + * @return {@code non-null;} the move result for the given instruction + */ + private SsaInsn getMoveForInsn(SsaInsn insn) { + int succ = insn.getBlock().getSuccessors().nextSetBit(0); + ArrayList<SsaInsn> succInsns = ssaMeth.getBlocks().get(succ).getInsns(); + return succInsns.get(0); + } + + /** + * Creates a link in the lattice between two EscapeSets due to a put + * instruction. The object being put is the child and the object being put + * into is the parent. A child set must always have an escape state at + * least as high as its parent. + * + * @param parentSet {@code non-null;} the EscapeSet for the object being put + * into + * @param childSet {@code non-null;} the EscapeSet for the object being put + */ + private void addEdge(EscapeSet parentSet, EscapeSet childSet) { + if (!childSet.parentSets.contains(parentSet)) { + childSet.parentSets.add(parentSet); } - - /** - * Finds the corresponding move result for a given instruction - * - * @param insn {@code non-null;} an instruction that must always be - * followed by a move result - * @return {@code non-null;} the move result for the given instruction - */ - private SsaInsn getMoveForInsn(SsaInsn insn) { - int succ = insn.getBlock().getSuccessors().nextSetBit(0); - ArrayList<SsaInsn> succInsns = ssaMeth.getBlocks().get(succ).getInsns(); - return succInsns.get(0); + if (!parentSet.childSets.contains(childSet)) { + parentSet.childSets.add(childSet); } - - /** - * Creates a link in the lattice between two EscapeSets due to a put - * instruction. The object being put is the child and the object being put - * into is the parent. A child set must always have an escape state at - * least as high as its parent. - * - * @param parentSet {@code non-null;} the EscapeSet for the object being put - * into - * @param childSet {@code non-null;} the EscapeSet for the object being put - */ - private void addEdge(EscapeSet parentSet, EscapeSet childSet) { - if (!childSet.parentSets.contains(parentSet)) { - childSet.parentSets.add(parentSet); - } - if (!parentSet.childSets.contains(childSet)) { - parentSet.childSets.add(childSet); - } + } + + /** + * Merges all links in the lattice among two EscapeSets. On return, the + * newNode will have its old links as well as all links from the oldNode. + * The oldNode has all its links removed. + * + * @param newNode {@code non-null;} the EscapeSet to merge all links into + * @param oldNode {@code non-null;} the EscapeSet to remove all links from + */ + private void replaceNode(EscapeSet newNode, EscapeSet oldNode) { + for (EscapeSet e : oldNode.parentSets) { + e.childSets.remove(oldNode); + e.childSets.add(newNode); + newNode.parentSets.add(e); } - - /** - * Merges all links in the lattice among two EscapeSets. On return, the - * newNode will have its old links as well as all links from the oldNode. - * The oldNode has all its links removed. - * - * @param newNode {@code non-null;} the EscapeSet to merge all links into - * @param oldNode {@code non-null;} the EscapeSet to remove all links from - */ - private void replaceNode(EscapeSet newNode, EscapeSet oldNode) { - for (EscapeSet e : oldNode.parentSets) { - e.childSets.remove(oldNode); - e.childSets.add(newNode); - newNode.parentSets.add(e); - } - for (EscapeSet e : oldNode.childSets) { - e.parentSets.remove(oldNode); - e.parentSets.add(newNode); - newNode.childSets.add(e); - } + for (EscapeSet e : oldNode.childSets) { + e.parentSets.remove(oldNode); + e.parentSets.add(newNode); + newNode.childSets.add(e); } - - /** - * Performs escape analysis on a method. Finds scalar replaceable arrays and - * replaces them with equivalent registers. - * - * @param ssaMethod {@code non-null;} method to process - */ - public static void process(SsaMethod ssaMethod) { - new EscapeAnalysis(ssaMethod).run(); + } + + /** + * Performs escape analysis on a method. Finds scalar replaceable arrays and + * replaces them with equivalent registers. + * + * @param ssaMethod {@code non-null;} method to process + */ + public static void process(SsaMethod ssaMethod) { + new EscapeAnalysis(ssaMethod).run(); + } + + /** + * Process a single instruction, looking for new objects resulting from + * move result or move param. + * + * @param insn {@code non-null;} instruction to process + */ + private void processInsn(SsaInsn insn) { + int op = insn.getOpcode().getOpcode(); + RegisterSpec result = insn.getResult(); + EscapeSet escSet; + + // Identify new objects + if (op == RegOps.MOVE_RESULT_PSEUDO + && result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { + // Handle objects generated through move_result_pseudo + escSet = processMoveResultPseudoInsn(insn); + processRegister(result, escSet); + } else if (op == RegOps.MOVE_PARAM && result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { + // Track method arguments that are objects + escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); + latticeValues.add(escSet); + processRegister(result, escSet); + } else if (op == RegOps.MOVE_RESULT + && result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { + // Track method return values that are objects + escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); + latticeValues.add(escSet); + processRegister(result, escSet); } - - /** - * Process a single instruction, looking for new objects resulting from - * move result or move param. - * - * @param insn {@code non-null;} instruction to process - */ - private void processInsn(SsaInsn insn) { - int op = insn.getOpcode().getOpcode(); - RegisterSpec result = insn.getResult(); - EscapeSet escSet; - - // Identify new objects - if (op == RegOps.MOVE_RESULT_PSEUDO && - result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { - // Handle objects generated through move_result_pseudo - escSet = processMoveResultPseudoInsn(insn); - processRegister(result, escSet); - } else if (op == RegOps.MOVE_PARAM && - result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { - // Track method arguments that are objects - escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); - latticeValues.add(escSet); - processRegister(result, escSet); - } else if (op == RegOps.MOVE_RESULT && - result.getTypeBearer().getBasicType() == Type.BT_OBJECT) { - // Track method return values that are objects - escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); - latticeValues.add(escSet); - processRegister(result, escSet); + } + + /** + * Determine the origin of a move result pseudo instruction that generates + * an object. Creates a new EscapeSet for the new object accordingly. + * + * @param insn {@code non-null;} move result pseudo instruction to process + * @return {@code non-null;} an EscapeSet for the object referred to by the + * move result pseudo instruction + */ + private EscapeSet processMoveResultPseudoInsn(SsaInsn insn) { + RegisterSpec result = insn.getResult(); + SsaInsn prevSsaInsn = getInsnForMove(insn); + int prevOpcode = prevSsaInsn.getOpcode().getOpcode(); + EscapeSet escSet; + RegisterSpec prevSource; + + switch (prevOpcode) { + // New instance / Constant + case RegOps.NEW_INSTANCE: + case RegOps.CONST: + escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); + break; + // New array + case RegOps.NEW_ARRAY: + case RegOps.FILLED_NEW_ARRAY: + prevSource = prevSsaInsn.getSources().get(0); + if (prevSource.getTypeBearer().isConstant()) { + // New fixed array + escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); + escSet.replaceableArray = true; + } else { + // New variable array + escSet = new EscapeSet(result.getReg(), regCount, EscapeState.GLOBAL); } - } - - /** - * Determine the origin of a move result pseudo instruction that generates - * an object. Creates a new EscapeSet for the new object accordingly. - * - * @param insn {@code non-null;} move result pseudo instruction to process - * @return {@code non-null;} an EscapeSet for the object referred to by the - * move result pseudo instruction - */ - private EscapeSet processMoveResultPseudoInsn(SsaInsn insn) { - RegisterSpec result = insn.getResult(); - SsaInsn prevSsaInsn = getInsnForMove(insn); - int prevOpcode = prevSsaInsn.getOpcode().getOpcode(); - EscapeSet escSet; - RegisterSpec prevSource; - - switch(prevOpcode) { - // New instance / Constant - case RegOps.NEW_INSTANCE: - case RegOps.CONST: - escSet = new EscapeSet(result.getReg(), regCount, - EscapeState.NONE); - break; - // New array - case RegOps.NEW_ARRAY: - case RegOps.FILLED_NEW_ARRAY: - prevSource = prevSsaInsn.getSources().get(0); - if (prevSource.getTypeBearer().isConstant()) { - // New fixed array - escSet = new EscapeSet(result.getReg(), regCount, - EscapeState.NONE); - escSet.replaceableArray = true; - } else { - // New variable array - escSet = new EscapeSet(result.getReg(), regCount, - EscapeState.GLOBAL); - } - break; - // Loading a static object - case RegOps.GET_STATIC: - escSet = new EscapeSet(result.getReg(), regCount, - EscapeState.GLOBAL); - break; - // Type cast / load an object from a field or array - case RegOps.CHECK_CAST: - case RegOps.GET_FIELD: - case RegOps.AGET: - prevSource = prevSsaInsn.getSources().get(0); - int setIndex = findSetIndex(prevSource); - - // Set should already exist, try to find it - if (setIndex != latticeValues.size()) { - escSet = latticeValues.get(setIndex); - escSet.regSet.set(result.getReg()); - return escSet; - } - - // Set not found, must be either null or unknown - if (prevSource.getType() == Type.KNOWN_NULL) { - escSet = new EscapeSet(result.getReg(), regCount, - EscapeState.NONE); - } else { - escSet = new EscapeSet(result.getReg(), regCount, - EscapeState.GLOBAL); - } - break; - default: - return null; + break; + // Loading a static object + case RegOps.GET_STATIC: + escSet = new EscapeSet(result.getReg(), regCount, EscapeState.GLOBAL); + break; + // Type cast / load an object from a field or array + case RegOps.CHECK_CAST: + case RegOps.GET_FIELD: + case RegOps.AGET: + prevSource = prevSsaInsn.getSources().get(0); + int setIndex = findSetIndex(prevSource); + + // Set should already exist, try to find it + if (setIndex != latticeValues.size()) { + escSet = latticeValues.get(setIndex); + escSet.regSet.set(result.getReg()); + return escSet; } - // Add the newly created escSet to the lattice and return it - latticeValues.add(escSet); - return escSet; - } - - /** - * Iterate through all the uses of a new object. - * - * @param result {@code non-null;} register where new object is stored - * @param escSet {@code non-null;} EscapeSet for the new object - */ - private void processRegister(RegisterSpec result, EscapeSet escSet) { - ArrayList<RegisterSpec> regWorklist = new ArrayList<RegisterSpec>(); - regWorklist.add(result); - - // Go through the worklist - while (!regWorklist.isEmpty()) { - int listSize = regWorklist.size() - 1; - RegisterSpec def = regWorklist.remove(listSize); - List<SsaInsn> useList = ssaMeth.getUseListForRegister(def.getReg()); - - // Handle all the uses of this register - for (SsaInsn use : useList) { - Rop useOpcode = use.getOpcode(); - - if (useOpcode == null) { - // Handle phis - processPhiUse(use, escSet, regWorklist); - } else { - // Handle other opcodes - processUse(def, use, escSet, regWorklist); - } - } + // Set not found, must be either null or unknown + if (prevSource.getType() == Type.KNOWN_NULL) { + escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE); + } else { + escSet = new EscapeSet(result.getReg(), regCount, EscapeState.GLOBAL); } + break; + default: + return null; } - /** - * Handles phi uses of new objects. Will merge together the sources of a phi - * into a single EscapeSet. Adds the result of the phi to the worklist so - * its uses can be followed. - * - * @param use {@code non-null;} phi use being processed - * @param escSet {@code non-null;} EscapeSet for the object - * @param regWorklist {@code non-null;} worklist of instructions left to - * process for this object - */ - private void processPhiUse(SsaInsn use, EscapeSet escSet, - ArrayList<RegisterSpec> regWorklist) { - int setIndex = findSetIndex(use.getResult()); - if (setIndex != latticeValues.size()) { - // Check if result is in a set already - EscapeSet mergeSet = latticeValues.get(setIndex); - if (mergeSet != escSet) { - // If it is, merge the sets and states, then delete the copy - escSet.replaceableArray = false; - escSet.regSet.or(mergeSet.regSet); - if (escSet.escape.compareTo(mergeSet.escape) < 0) { - escSet.escape = mergeSet.escape; - } - replaceNode(escSet, mergeSet); - latticeValues.remove(setIndex); - } + // Add the newly created escSet to the lattice and return it + latticeValues.add(escSet); + return escSet; + } + + /** + * Iterate through all the uses of a new object. + * + * @param result {@code non-null;} register where new object is stored + * @param escSet {@code non-null;} EscapeSet for the new object + */ + private void processRegister(RegisterSpec result, EscapeSet escSet) { + ArrayList<RegisterSpec> regWorklist = new ArrayList<RegisterSpec>(); + regWorklist.add(result); + + // Go through the worklist + while (!regWorklist.isEmpty()) { + int listSize = regWorklist.size() - 1; + RegisterSpec def = regWorklist.remove(listSize); + List<SsaInsn> useList = ssaMeth.getUseListForRegister(def.getReg()); + + // Handle all the uses of this register + for (SsaInsn use : useList) { + Rop useOpcode = use.getOpcode(); + + if (useOpcode == null) { + // Handle phis + processPhiUse(use, escSet, regWorklist); } else { - // If no set is found, add it to this escSet and the worklist - escSet.regSet.set(use.getResult().getReg()); - regWorklist.add(use.getResult()); + // Handle other opcodes + processUse(def, use, escSet, regWorklist); } + } } - - /** - * Handles non-phi uses of new objects. Checks to see how instruction is - * used and updates the escape state accordingly. - * - * @param def {@code non-null;} register holding definition of new object - * @param use {@code non-null;} use of object being processed - * @param escSet {@code non-null;} EscapeSet for the object - * @param regWorklist {@code non-null;} worklist of instructions left to - * process for this object - */ - private void processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet, - ArrayList<RegisterSpec> regWorklist) { - int useOpcode = use.getOpcode().getOpcode(); - switch (useOpcode) { - case RegOps.MOVE: - // Follow uses of the move by adding it to the worklist - escSet.regSet.set(use.getResult().getReg()); - regWorklist.add(use.getResult()); - break; - case RegOps.IF_EQ: - case RegOps.IF_NE: - case RegOps.CHECK_CAST: - // Compared objects can't be replaced, so promote if necessary - if (escSet.escape.compareTo(EscapeState.METHOD) < 0) { - escSet.escape = EscapeState.METHOD; - } - break; - case RegOps.APUT: - // For array puts, check for a constant array index - RegisterSpec putIndex = use.getSources().get(2); - if (!putIndex.getTypeBearer().isConstant()) { - // If not constant, array can't be replaced - escSet.replaceableArray = false; - } - // Intentional fallthrough - case RegOps.PUT_FIELD: - // Skip non-object puts - RegisterSpec putValue = use.getSources().get(0); - if (putValue.getTypeBearer().getBasicType() != Type.BT_OBJECT) { - break; - } - escSet.replaceableArray = false; - - // Raise 1st object's escape state to 2nd if 2nd is higher - RegisterSpecList sources = use.getSources(); - if (sources.get(0).getReg() == def.getReg()) { - int setIndex = findSetIndex(sources.get(1)); - if (setIndex != latticeValues.size()) { - EscapeSet parentSet = latticeValues.get(setIndex); - addEdge(parentSet, escSet); - if (escSet.escape.compareTo(parentSet.escape) < 0) { - escSet.escape = parentSet.escape; - } - } - } else { - int setIndex = findSetIndex(sources.get(0)); - if (setIndex != latticeValues.size()) { - EscapeSet childSet = latticeValues.get(setIndex); - addEdge(escSet, childSet); - if (childSet.escape.compareTo(escSet.escape) < 0) { - childSet.escape = escSet.escape; - } - } - } - break; - case RegOps.AGET: - // For array gets, check for a constant array index - RegisterSpec getIndex = use.getSources().get(1); - if (!getIndex.getTypeBearer().isConstant()) { - // If not constant, array can't be replaced - escSet.replaceableArray = false; - } - break; - case RegOps.PUT_STATIC: - // Static puts cause an object to escape globally - escSet.escape = EscapeState.GLOBAL; - break; - case RegOps.INVOKE_STATIC: - case RegOps.INVOKE_VIRTUAL: - case RegOps.INVOKE_SUPER: - case RegOps.INVOKE_DIRECT: - case RegOps.INVOKE_INTERFACE: - case RegOps.RETURN: - case RegOps.THROW: - // These operations cause an object to escape interprocedurally - escSet.escape = EscapeState.INTER; - break; - default: - break; + } + + /** + * Handles phi uses of new objects. Will merge together the sources of a phi + * into a single EscapeSet. Adds the result of the phi to the worklist so + * its uses can be followed. + * + * @param use {@code non-null;} phi use being processed + * @param escSet {@code non-null;} EscapeSet for the object + * @param regWorklist {@code non-null;} worklist of instructions left to + * process for this object + */ + private void processPhiUse(SsaInsn use, EscapeSet escSet, ArrayList<RegisterSpec> regWorklist) { + int setIndex = findSetIndex(use.getResult()); + if (setIndex != latticeValues.size()) { + // Check if result is in a set already + EscapeSet mergeSet = latticeValues.get(setIndex); + if (mergeSet != escSet) { + // If it is, merge the sets and states, then delete the copy + escSet.replaceableArray = false; + escSet.regSet.or(mergeSet.regSet); + if (escSet.escape.compareTo(mergeSet.escape) < 0) { + escSet.escape = mergeSet.escape; } + replaceNode(escSet, mergeSet); + latticeValues.remove(setIndex); + } + } else { + // If no set is found, add it to this escSet and the worklist + escSet.regSet.set(use.getResult().getReg()); + regWorklist.add(use.getResult()); } - - /** - * Performs scalar replacement on all eligible arrays. - */ - private void scalarReplacement() { - // Iterate through lattice, looking for non-escaping replaceable arrays - for (EscapeSet escSet : latticeValues) { - if (!escSet.replaceableArray || escSet.escape != EscapeState.NONE) { - continue; + } + + /** + * Handles non-phi uses of new objects. Checks to see how instruction is + * used and updates the escape state accordingly. + * + * @param def {@code non-null;} register holding definition of new object + * @param use {@code non-null;} use of object being processed + * @param escSet {@code non-null;} EscapeSet for the object + * @param regWorklist {@code non-null;} worklist of instructions left to + * process for this object + */ + private void processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet, + ArrayList<RegisterSpec> regWorklist) { + int useOpcode = use.getOpcode().getOpcode(); + switch (useOpcode) { + case RegOps.MOVE: + // Follow uses of the move by adding it to the worklist + escSet.regSet.set(use.getResult().getReg()); + regWorklist.add(use.getResult()); + break; + case RegOps.IF_EQ: + case RegOps.IF_NE: + case RegOps.CHECK_CAST: + // Compared objects can't be replaced, so promote if necessary + if (escSet.escape.compareTo(EscapeState.METHOD) < 0) { + escSet.escape = EscapeState.METHOD; + } + break; + case RegOps.APUT: + // For array puts, check for a constant array index + RegisterSpec putIndex = use.getSources().get(2); + if (!putIndex.getTypeBearer().isConstant()) { + // If not constant, array can't be replaced + escSet.replaceableArray = false; + } + // Intentional fallthrough + case RegOps.PUT_FIELD: + // Skip non-object puts + RegisterSpec putValue = use.getSources().get(0); + if (putValue.getTypeBearer().getBasicType() != Type.BT_OBJECT) { + break; + } + escSet.replaceableArray = false; + + // Raise 1st object's escape state to 2nd if 2nd is higher + RegisterSpecList sources = use.getSources(); + if (sources.get(0).getReg() == def.getReg()) { + int setIndex = findSetIndex(sources.get(1)); + if (setIndex != latticeValues.size()) { + EscapeSet parentSet = latticeValues.get(setIndex); + addEdge(parentSet, escSet); + if (escSet.escape.compareTo(parentSet.escape) < 0) { + escSet.escape = parentSet.escape; } - - // Get the instructions for the definition and move of the array - int e = escSet.regSet.nextSetBit(0); - SsaInsn def = ssaMeth.getDefinitionForRegister(e); - SsaInsn prev = getInsnForMove(def); - - // Create a map for the new registers that will be created - TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer(); - int length = ((CstLiteralBits) lengthReg).getIntBits(); - ArrayList<RegisterSpec> newRegs = - new ArrayList<RegisterSpec>(length); - HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>(); - - // Replace the definition of the array with registers - replaceDef(def, prev, length, newRegs); - - // Mark definition instructions for deletion - deletedInsns.add(prev); - deletedInsns.add(def); - - // Go through all uses of the array - List<SsaInsn> useList = ssaMeth.getUseListForRegister(e); - for (SsaInsn use : useList) { - // Replace the use with scalars and then mark it for deletion - replaceUse(use, prev, newRegs, deletedInsns); - deletedInsns.add(use); + } + } else { + int setIndex = findSetIndex(sources.get(0)); + if (setIndex != latticeValues.size()) { + EscapeSet childSet = latticeValues.get(setIndex); + addEdge(escSet, childSet); + if (childSet.escape.compareTo(escSet.escape) < 0) { + childSet.escape = escSet.escape; } - - // Delete all marked instructions - ssaMeth.deleteInsns(deletedInsns); - ssaMeth.onInsnsChanged(); - - // Convert the method back to SSA form - SsaConverter.updateSsaMethod(ssaMeth, regCount); - - // Propagate and remove extra moves added by scalar replacement - movePropagate(); + } } + break; + case RegOps.AGET: + // For array gets, check for a constant array index + RegisterSpec getIndex = use.getSources().get(1); + if (!getIndex.getTypeBearer().isConstant()) { + // If not constant, array can't be replaced + escSet.replaceableArray = false; + } + break; + case RegOps.PUT_STATIC: + // Static puts cause an object to escape globally + escSet.escape = EscapeState.GLOBAL; + break; + case RegOps.INVOKE_STATIC: + case RegOps.INVOKE_VIRTUAL: + case RegOps.INVOKE_SUPER: + case RegOps.INVOKE_DIRECT: + case RegOps.INVOKE_INTERFACE: + case RegOps.RETURN: + case RegOps.THROW: + // These operations cause an object to escape interprocedurally + escSet.escape = EscapeState.INTER; + break; + default: + break; } - - /** - * Replaces the instructions that define an array with equivalent registers. - * For each entry in the array, a register is created, initialized to zero. - * A mapping between this register and the corresponding array index is - * added. - * - * @param def {@code non-null;} move result instruction for array - * @param prev {@code non-null;} instruction for instantiating new array - * @param length size of the new array - * @param newRegs {@code non-null;} mapping of array indices to new - * registers to be populated - */ - private void replaceDef(SsaInsn def, SsaInsn prev, int length, - ArrayList<RegisterSpec> newRegs) { - Type resultType = def.getResult().getType(); - - // Create new zeroed out registers for each element in the array + } + + /** + * Performs scalar replacement on all eligible arrays. + */ + private void scalarReplacement() { + // Iterate through lattice, looking for non-escaping replaceable arrays + for (EscapeSet escSet : latticeValues) { + if (!escSet.replaceableArray || escSet.escape != EscapeState.NONE) { + continue; + } + + // Get the instructions for the definition and move of the array + int e = escSet.regSet.nextSetBit(0); + SsaInsn def = ssaMeth.getDefinitionForRegister(e); + SsaInsn prev = getInsnForMove(def); + + // Create a map for the new registers that will be created + TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer(); + int length = ((CstLiteralBits) lengthReg).getIntBits(); + ArrayList<RegisterSpec> newRegs = new ArrayList<RegisterSpec>(length); + HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>(); + + // Replace the definition of the array with registers + replaceDef(def, prev, length, newRegs); + + // Mark definition instructions for deletion + deletedInsns.add(prev); + deletedInsns.add(def); + + // Go through all uses of the array + List<SsaInsn> useList = ssaMeth.getUseListForRegister(e); + for (SsaInsn use : useList) { + // Replace the use with scalars and then mark it for deletion + replaceUse(use, prev, newRegs, deletedInsns); + deletedInsns.add(use); + } + + // Delete all marked instructions + ssaMeth.deleteInsns(deletedInsns); + ssaMeth.onInsnsChanged(); + + // Convert the method back to SSA form + SsaConverter.updateSsaMethod(ssaMeth, regCount); + + // Propagate and remove extra moves added by scalar replacement + movePropagate(); + } + } + + /** + * Replaces the instructions that define an array with equivalent registers. + * For each entry in the array, a register is created, initialized to zero. + * A mapping between this register and the corresponding array index is + * added. + * + * @param def {@code non-null;} move result instruction for array + * @param prev {@code non-null;} instruction for instantiating new array + * @param length size of the new array + * @param newRegs {@code non-null;} mapping of array indices to new + * registers to be populated + */ + private void replaceDef(SsaInsn def, SsaInsn prev, int length, ArrayList<RegisterSpec> newRegs) { + Type resultType = def.getResult().getType(); + + // Create new zeroed out registers for each element in the array + for (int i = 0; i < length; i++) { + Constant newZero = Zeroes.zeroFor(resultType.getComponentType()); + TypedConstant typedZero = (TypedConstant) newZero; + RegisterSpec newReg = RegisterSpec.make(ssaMeth.makeNewSsaReg(), typedZero); + newRegs.add(newReg); + insertPlainInsnBefore(def, RegisterSpecList.EMPTY, newReg, RegOps.CONST, newZero); + } + } + + /** + * Replaces the use for a scalar replaceable array. Gets and puts become + * move instructions, and array lengths and fills are handled. Can also + * identify ArrayIndexOutOfBounds exceptions and throw them if detected. + * + * @param use {@code non-null;} move result instruction for array + * @param prev {@code non-null;} instruction for instantiating new array + * @param newRegs {@code non-null;} mapping of array indices to new + * registers + * @param deletedInsns {@code non-null;} set of instructions marked for + * deletion + */ + private void replaceUse(SsaInsn use, SsaInsn prev, ArrayList<RegisterSpec> newRegs, + HashSet<SsaInsn> deletedInsns) { + int index; + int length = newRegs.size(); + SsaInsn next; + RegisterSpecList sources; + RegisterSpec source, result; + CstLiteralBits indexReg; + + switch (use.getOpcode().getOpcode()) { + case RegOps.AGET: + // Replace array gets with moves + next = getMoveForInsn(use); + sources = use.getSources(); + indexReg = ((CstLiteralBits) sources.get(1).getTypeBearer()); + index = indexReg.getIntBits(); + if (index < length) { + source = newRegs.get(index); + result = source.withReg(next.getResult().getReg()); + insertPlainInsnBefore(next, RegisterSpecList.make(source), result, RegOps.MOVE, null); + } else { + // Throw an exception if the index is out of bounds + insertExceptionThrow(next, sources.get(1), deletedInsns); + deletedInsns.add(next.getBlock().getInsns().get(2)); + } + deletedInsns.add(next); + break; + case RegOps.APUT: + // Replace array puts with moves + sources = use.getSources(); + indexReg = ((CstLiteralBits) sources.get(2).getTypeBearer()); + index = indexReg.getIntBits(); + if (index < length) { + source = sources.get(0); + result = source.withReg(newRegs.get(index).getReg()); + insertPlainInsnBefore(use, RegisterSpecList.make(source), result, RegOps.MOVE, null); + // Update the newReg entry to mark value as unknown now + newRegs.set(index, result.withSimpleType()); + } else { + // Throw an exception if the index is out of bounds + insertExceptionThrow(use, sources.get(2), deletedInsns); + } + break; + case RegOps.ARRAY_LENGTH: + // Replace array lengths with const instructions + TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer(); + //CstInteger lengthReg = CstInteger.make(length); + next = getMoveForInsn(use); + insertPlainInsnBefore(next, RegisterSpecList.EMPTY, next.getResult(), RegOps.CONST, + (Constant) lengthReg); + deletedInsns.add(next); + break; + case RegOps.MARK_LOCAL: + // Remove mark local instructions + break; + case RegOps.FILL_ARRAY_DATA: + // Create const instructions for each fill value + Insn ropUse = use.getOriginalRopInsn(); + FillArrayDataInsn fill = (FillArrayDataInsn) ropUse; + ArrayList<Constant> constList = fill.getInitValues(); for (int i = 0; i < length; i++) { - Constant newZero = Zeroes.zeroFor(resultType.getComponentType()); - TypedConstant typedZero = (TypedConstant) newZero; - RegisterSpec newReg = - RegisterSpec.make(ssaMeth.makeNewSsaReg(), typedZero); - newRegs.add(newReg); - insertPlainInsnBefore(def, RegisterSpecList.EMPTY, newReg, - RegOps.CONST, newZero); + RegisterSpec newFill = + RegisterSpec.make(newRegs.get(i).getReg(), (TypeBearer) constList.get(i)); + insertPlainInsnBefore(use, RegisterSpecList.EMPTY, newFill, RegOps.CONST, + constList.get(i)); + // Update the newRegs to hold the new const value + newRegs.set(i, newFill); } + break; + default: } - - /** - * Replaces the use for a scalar replaceable array. Gets and puts become - * move instructions, and array lengths and fills are handled. Can also - * identify ArrayIndexOutOfBounds exceptions and throw them if detected. - * - * @param use {@code non-null;} move result instruction for array - * @param prev {@code non-null;} instruction for instantiating new array - * @param newRegs {@code non-null;} mapping of array indices to new - * registers - * @param deletedInsns {@code non-null;} set of instructions marked for - * deletion - */ - private void replaceUse(SsaInsn use, SsaInsn prev, - ArrayList<RegisterSpec> newRegs, - HashSet<SsaInsn> deletedInsns) { - int index; - int length = newRegs.size(); - SsaInsn next; - RegisterSpecList sources; - RegisterSpec source, result; - CstLiteralBits indexReg; - - switch (use.getOpcode().getOpcode()) { - case RegOps.AGET: - // Replace array gets with moves - next = getMoveForInsn(use); - sources = use.getSources(); - indexReg = ((CstLiteralBits) sources.get(1).getTypeBearer()); - index = indexReg.getIntBits(); - if (index < length) { - source = newRegs.get(index); - result = source.withReg(next.getResult().getReg()); - insertPlainInsnBefore(next, RegisterSpecList.make(source), - result, RegOps.MOVE, null); - } else { - // Throw an exception if the index is out of bounds - insertExceptionThrow(next, sources.get(1), deletedInsns); - deletedInsns.add(next.getBlock().getInsns().get(2)); - } - deletedInsns.add(next); - break; - case RegOps.APUT: - // Replace array puts with moves - sources = use.getSources(); - indexReg = ((CstLiteralBits) sources.get(2).getTypeBearer()); - index = indexReg.getIntBits(); - if (index < length) { - source = sources.get(0); - result = source.withReg(newRegs.get(index).getReg()); - insertPlainInsnBefore(use, RegisterSpecList.make(source), - result, RegOps.MOVE, null); - // Update the newReg entry to mark value as unknown now - newRegs.set(index, result.withSimpleType()); - } else { - // Throw an exception if the index is out of bounds - insertExceptionThrow(use, sources.get(2), deletedInsns); - } - break; - case RegOps.ARRAY_LENGTH: - // Replace array lengths with const instructions - TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer(); - //CstInteger lengthReg = CstInteger.make(length); - next = getMoveForInsn(use); - insertPlainInsnBefore(next, RegisterSpecList.EMPTY, - next.getResult(), RegOps.CONST, - (Constant) lengthReg); - deletedInsns.add(next); - break; - case RegOps.MARK_LOCAL: - // Remove mark local instructions - break; - case RegOps.FILL_ARRAY_DATA: - // Create const instructions for each fill value - Insn ropUse = use.getOriginalRopInsn(); - FillArrayDataInsn fill = (FillArrayDataInsn) ropUse; - ArrayList<Constant> constList = fill.getInitValues(); - for (int i = 0; i < length; i++) { - RegisterSpec newFill = - RegisterSpec.make(newRegs.get(i).getReg(), - (TypeBearer) constList.get(i)); - insertPlainInsnBefore(use, RegisterSpecList.EMPTY, newFill, - RegOps.CONST, constList.get(i)); - // Update the newRegs to hold the new const value - newRegs.set(i, newFill); - } - break; - default: + } + + /** + * Identifies extra moves added by scalar replacement and propagates the + * source of the move to any users of the result. + */ + private void movePropagate() { + for (int i = 0; i < ssaMeth.getRegCount(); i++) { + SsaInsn insn = ssaMeth.getDefinitionForRegister(i); + + // Look for move instructions only + if (insn == null || insn.getOpcode() == null || insn.getOpcode().getOpcode() != RegOps.MOVE) { + continue; + } + + final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy(); + final RegisterSpec source = insn.getSources().get(0); + final RegisterSpec result = insn.getResult(); + + // Ignore moves that weren't added due to scalar replacement + if (source.getReg() < regCount && result.getReg() < regCount) { + continue; + } + + // Create a mapping from source to result + RegisterMapper mapper = new RegisterMapper() { + @Override + public int getNewRegisterCount() { + return ssaMeth.getRegCount(); } - } - /** - * Identifies extra moves added by scalar replacement and propagates the - * source of the move to any users of the result. - */ - private void movePropagate() { - for (int i = 0; i < ssaMeth.getRegCount(); i++) { - SsaInsn insn = ssaMeth.getDefinitionForRegister(i); - - // Look for move instructions only - if (insn == null || insn.getOpcode() == null || - insn.getOpcode().getOpcode() != RegOps.MOVE) { - continue; - } + @Override + public RegisterSpec map(RegisterSpec registerSpec) { + if (registerSpec.getReg() == result.getReg()) { + return source; + } - final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy(); - final RegisterSpec source = insn.getSources().get(0); - final RegisterSpec result = insn.getResult(); - - // Ignore moves that weren't added due to scalar replacement - if (source.getReg() < regCount && result.getReg() < regCount) { - continue; - } - - // Create a mapping from source to result - RegisterMapper mapper = new RegisterMapper() { - @Override - public int getNewRegisterCount() { - return ssaMeth.getRegCount(); - } - - @Override - public RegisterSpec map(RegisterSpec registerSpec) { - if (registerSpec.getReg() == result.getReg()) { - return source; - } - - return registerSpec; - } - }; - - // Modify all uses of the move to use the source of the move instead - for (SsaInsn use : useList[result.getReg()]) { - use.mapSourceRegisters(mapper); - } + return registerSpec; } - } + }; - /** - * Runs escape analysis and scalar replacement of arrays. - */ - private void run() { - ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() { - public void visitBlock (SsaBasicBlock block, - SsaBasicBlock unused) { - block.forEachInsn(new SsaInsn.Visitor() { - public void visitMoveInsn(NormalSsaInsn insn) { - // do nothing - } - - public void visitPhiInsn(PhiInsn insn) { - // do nothing - } - - public void visitNonMoveInsn(NormalSsaInsn insn) { - processInsn(insn); - } - }); - } + // Modify all uses of the move to use the source of the move instead + for (SsaInsn use : useList[result.getReg()]) { + use.mapSourceRegisters(mapper); + } + } + } + + /** + * Runs escape analysis and scalar replacement of arrays. + */ + private void run() { + ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() { + @Override + public void visitBlock(SsaBasicBlock block, SsaBasicBlock unused) { + block.forEachInsn(new SsaInsn.Visitor() { + @Override + public void visitMoveInsn(NormalSsaInsn insn) { + // do nothing + } + + @Override + public void visitPhiInsn(PhiInsn insn) { + // do nothing + } + + @Override + public void visitNonMoveInsn(NormalSsaInsn insn) { + processInsn(insn); + } }); - - // Go through lattice and promote fieldSets as necessary - for (EscapeSet e : latticeValues) { - if (e.escape != EscapeState.NONE) { - for (EscapeSet field : e.childSets) { - if (e.escape.compareTo(field.escape) > 0) { - field.escape = e.escape; - } - } - } + } + }); + + // Go through lattice and promote fieldSets as necessary + for (EscapeSet e : latticeValues) { + if (e.escape != EscapeState.NONE) { + for (EscapeSet field : e.childSets) { + if (e.escape.compareTo(field.escape) > 0) { + field.escape = e.escape; + } } - - // Perform scalar replacement for arrays - scalarReplacement(); + } } - /** - * Replaces instructions that trigger an ArrayIndexOutofBounds exception - * with an actual throw of the exception. - * - * @param insn {@code non-null;} instruction causing the exception - * @param index {@code non-null;} index value that is out of bounds - * @param deletedInsns {@code non-null;} set of instructions marked for - * deletion - */ - private void insertExceptionThrow(SsaInsn insn, RegisterSpec index, - HashSet<SsaInsn> deletedInsns) { - // Create a new ArrayIndexOutOfBoundsException - CstType exception = - new CstType(Exceptions.TYPE_ArrayIndexOutOfBoundsException); - insertThrowingInsnBefore(insn, RegisterSpecList.EMPTY, null, - RegOps.NEW_INSTANCE, exception); - - // Add a successor block with a move result pseudo for the exception - SsaBasicBlock currBlock = insn.getBlock(); - SsaBasicBlock newBlock = - currBlock.insertNewSuccessor(currBlock.getPrimarySuccessor()); - SsaInsn newInsn = newBlock.getInsns().get(0); - RegisterSpec newReg = - RegisterSpec.make(ssaMeth.makeNewSsaReg(), exception); - insertPlainInsnBefore(newInsn, RegisterSpecList.EMPTY, newReg, - RegOps.MOVE_RESULT_PSEUDO, null); - - // Add another successor block to initialize the exception - SsaBasicBlock newBlock2 = - newBlock.insertNewSuccessor(newBlock.getPrimarySuccessor()); - SsaInsn newInsn2 = newBlock2.getInsns().get(0); - CstNat newNat = new CstNat(new CstString("<init>"), new CstString("(I)V")); - CstMethodRef newRef = new CstMethodRef(exception, newNat); - insertThrowingInsnBefore(newInsn2, RegisterSpecList.make(newReg, index), - null, RegOps.INVOKE_DIRECT, newRef); - deletedInsns.add(newInsn2); - - // Add another successor block to throw the new exception - SsaBasicBlock newBlock3 = - newBlock2.insertNewSuccessor(newBlock2.getPrimarySuccessor()); - SsaInsn newInsn3 = newBlock3.getInsns().get(0); - insertThrowingInsnBefore(newInsn3, RegisterSpecList.make(newReg), null, - RegOps.THROW, null); - newBlock3.replaceSuccessor(newBlock3.getPrimarySuccessorIndex(), - ssaMeth.getExitBlock().getIndex()); - deletedInsns.add(newInsn3); + // Perform scalar replacement for arrays + scalarReplacement(); + } + + /** + * Replaces instructions that trigger an ArrayIndexOutofBounds exception + * with an actual throw of the exception. + * + * @param insn {@code non-null;} instruction causing the exception + * @param index {@code non-null;} index value that is out of bounds + * @param deletedInsns {@code non-null;} set of instructions marked for + * deletion + */ + private void insertExceptionThrow(SsaInsn insn, RegisterSpec index, + HashSet<SsaInsn> deletedInsns) { + // Create a new ArrayIndexOutOfBoundsException + CstType exception = new CstType(Exceptions.TYPE_ArrayIndexOutOfBoundsException); + insertThrowingInsnBefore(insn, RegisterSpecList.EMPTY, null, RegOps.NEW_INSTANCE, exception); + + // Add a successor block with a move result pseudo for the exception + SsaBasicBlock currBlock = insn.getBlock(); + SsaBasicBlock newBlock = currBlock.insertNewSuccessor(currBlock.getPrimarySuccessor()); + SsaInsn newInsn = newBlock.getInsns().get(0); + RegisterSpec newReg = RegisterSpec.make(ssaMeth.makeNewSsaReg(), exception); + insertPlainInsnBefore(newInsn, RegisterSpecList.EMPTY, newReg, RegOps.MOVE_RESULT_PSEUDO, null); + + // Add another successor block to initialize the exception + SsaBasicBlock newBlock2 = newBlock.insertNewSuccessor(newBlock.getPrimarySuccessor()); + SsaInsn newInsn2 = newBlock2.getInsns().get(0); + CstNat newNat = new CstNat(new CstString("<init>"), new CstString("(I)V")); + CstMethodRef newRef = new CstMethodRef(exception, newNat); + insertThrowingInsnBefore(newInsn2, RegisterSpecList.make(newReg, index), null, + RegOps.INVOKE_DIRECT, newRef); + deletedInsns.add(newInsn2); + + // Add another successor block to throw the new exception + SsaBasicBlock newBlock3 = newBlock2.insertNewSuccessor(newBlock2.getPrimarySuccessor()); + SsaInsn newInsn3 = newBlock3.getInsns().get(0); + insertThrowingInsnBefore(newInsn3, RegisterSpecList.make(newReg), null, RegOps.THROW, null); + newBlock3.replaceSuccessor(newBlock3.getPrimarySuccessorIndex(), + ssaMeth.getExitBlock().getIndex()); + deletedInsns.add(newInsn3); + } + + /** + * Inserts a new PlainInsn before the given instruction. + * TODO(dx team): move this somewhere more appropriate + * + * @param insn {@code non-null;} instruction to insert before + * @param newSources {@code non-null;} sources of new instruction + * @param newResult {@code non-null;} result of new instruction + * @param newOpcode opcode of new instruction + * @param cst {@code null-ok;} constant for new instruction, if any + */ + private void insertPlainInsnBefore(SsaInsn insn, RegisterSpecList newSources, + RegisterSpec newResult, int newOpcode, Constant cst) { + + Insn originalRopInsn = insn.getOriginalRopInsn(); + Rop newRop; + if (newOpcode == RegOps.MOVE_RESULT_PSEUDO) { + newRop = Rops.opMoveResultPseudo(newResult.getType()); + } else { + newRop = Rops.ropFor(newOpcode, newResult, newSources, cst); } - /** - * Inserts a new PlainInsn before the given instruction. - * TODO: move this somewhere more appropriate - * - * @param insn {@code non-null;} instruction to insert before - * @param newSources {@code non-null;} sources of new instruction - * @param newResult {@code non-null;} result of new instruction - * @param newOpcode opcode of new instruction - * @param cst {@code null-ok;} constant for new instruction, if any - */ - private void insertPlainInsnBefore(SsaInsn insn, - RegisterSpecList newSources, RegisterSpec newResult, int newOpcode, - Constant cst) { - - Insn originalRopInsn = insn.getOriginalRopInsn(); - Rop newRop; - if (newOpcode == RegOps.MOVE_RESULT_PSEUDO) { - newRop = Rops.opMoveResultPseudo(newResult.getType()); - } else { - newRop = Rops.ropFor(newOpcode, newResult, newSources, cst); - } - - Insn newRopInsn; - if (cst == null) { - newRopInsn = new PlainInsn(newRop, - originalRopInsn.getPosition(), newResult, newSources); - } else { - newRopInsn = new PlainCstInsn(newRop, - originalRopInsn.getPosition(), newResult, newSources, cst); - } - - NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); - List<SsaInsn> insns = insn.getBlock().getInsns(); - - insns.add(insns.lastIndexOf(insn), newInsn); - ssaMeth.onInsnAdded(newInsn); + Insn newRopInsn; + if (cst == null) { + newRopInsn = new PlainInsn(newRop, originalRopInsn.getPosition(), newResult, newSources); + } else { + newRopInsn = + new PlainCstInsn(newRop, originalRopInsn.getPosition(), newResult, newSources, cst); } - /** - * Inserts a new ThrowingInsn before the given instruction. - * TODO: move this somewhere more appropriate - * - * @param insn {@code non-null;} instruction to insert before - * @param newSources {@code non-null;} sources of new instruction - * @param newResult {@code non-null;} result of new instruction - * @param newOpcode opcode of new instruction - * @param cst {@code null-ok;} constant for new instruction, if any - */ - private void insertThrowingInsnBefore(SsaInsn insn, - RegisterSpecList newSources, RegisterSpec newResult, int newOpcode, - Constant cst) { - - Insn origRopInsn = insn.getOriginalRopInsn(); - Rop newRop = Rops.ropFor(newOpcode, newResult, newSources, cst); - Insn newRopInsn; - if (cst == null) { - newRopInsn = new ThrowingInsn(newRop, - origRopInsn.getPosition(), newSources, StdTypeList.EMPTY); - } else { - newRopInsn = new ThrowingCstInsn(newRop, - origRopInsn.getPosition(), newSources, StdTypeList.EMPTY, cst); - } + NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); + List<SsaInsn> insns = insn.getBlock().getInsns(); + + insns.add(insns.lastIndexOf(insn), newInsn); + ssaMeth.onInsnAdded(newInsn); + } + + /** + * Inserts a new ThrowingInsn before the given instruction. + * TODO(dx team): move this somewhere more appropriate + * + * @param insn {@code non-null;} instruction to insert before + * @param newSources {@code non-null;} sources of new instruction + * @param newResult {@code non-null;} result of new instruction + * @param newOpcode opcode of new instruction + * @param cst {@code null-ok;} constant for new instruction, if any + */ + private void insertThrowingInsnBefore(SsaInsn insn, RegisterSpecList newSources, + RegisterSpec newResult, int newOpcode, Constant cst) { + + Insn origRopInsn = insn.getOriginalRopInsn(); + Rop newRop = Rops.ropFor(newOpcode, newResult, newSources, cst); + Insn newRopInsn; + if (cst == null) { + newRopInsn = + new ThrowingInsn(newRop, origRopInsn.getPosition(), newSources, StdTypeList.EMPTY); + } else { + newRopInsn = new ThrowingCstInsn(newRop, origRopInsn.getPosition(), newSources, + StdTypeList.EMPTY, cst); + } - NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); - List<SsaInsn> insns = insn.getBlock().getInsns(); + NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); + List<SsaInsn> insns = insn.getBlock().getInsns(); - insns.add(insns.lastIndexOf(insn), newInsn); - ssaMeth.onInsnAdded(newInsn); - } + insns.add(insns.lastIndexOf(insn), newInsn); + ssaMeth.onInsnAdded(newInsn); + } } diff --git a/dx/src/com/android/jack/dx/ssa/InterferenceRegisterMapper.java b/dx/src/com/android/jack/dx/ssa/InterferenceRegisterMapper.java index 589e2f2..0f3f845 100644 --- a/dx/src/com/android/jack/dx/ssa/InterferenceRegisterMapper.java +++ b/dx/src/com/android/jack/dx/ssa/InterferenceRegisterMapper.java @@ -23,7 +23,6 @@ import com.android.jack.dx.util.BitIntSet; import com.android.jack.dx.util.IntSet; import java.util.ArrayList; -import java.util.BitSet; /** * A register mapper that keeps track of the accumulated interference @@ -33,132 +32,127 @@ import java.util.BitSet; * have variable register widths/categories, and the new namespace does. */ public class InterferenceRegisterMapper extends BasicRegisterMapper { - /** - * Array of interference sets. ArrayList is indexed by new namespace - * and BitIntSet's are indexed by old namespace. The list expands - * as needed and missing items are assumed to interfere with nothing. - * - * Bit sets are always used here, unlike elsewhere, because the max - * size of this matrix will be (countSsaRegs * countRopRegs), which may - * grow to hundreds of K but not megabytes. - */ - private final ArrayList<BitIntSet> newRegInterference; - - /** the interference graph for the old namespace */ - private final InterferenceGraph oldRegInterference; - - /** - * Constructs an instance - * - * @param countOldRegisters number of registers in old namespace - */ - public InterferenceRegisterMapper(InterferenceGraph oldRegInterference, - int countOldRegisters) { - super(countOldRegisters); - - newRegInterference = new ArrayList<BitIntSet>(); - this.oldRegInterference = oldRegInterference; + /** + * Array of interference sets. ArrayList is indexed by new namespace + * and BitIntSet's are indexed by old namespace. The list expands + * as needed and missing items are assumed to interfere with nothing. + * + * Bit sets are always used here, unlike elsewhere, because the max + * size of this matrix will be (countSsaRegs * countRopRegs), which may + * grow to hundreds of K but not megabytes. + */ + private final ArrayList<BitIntSet> newRegInterference; + + /** the interference graph for the old namespace */ + private final InterferenceGraph oldRegInterference; + + /** + * Constructs an instance + * + * @param countOldRegisters number of registers in old namespace + */ + public InterferenceRegisterMapper(InterferenceGraph oldRegInterference, int countOldRegisters) { + super(countOldRegisters); + + newRegInterference = new ArrayList<BitIntSet>(); + this.oldRegInterference = oldRegInterference; + } + + /** {@inheritDoc} */ + @Override + public void addMapping(int oldReg, int newReg, int category) { + super.addMapping(oldReg, newReg, category); + + addInterfence(newReg, oldReg); + + if (category == 2) { + addInterfence(newReg + 1, oldReg); } - - /** {@inheritDoc} */ - @Override - public void addMapping(int oldReg, int newReg, int category) { - super.addMapping(oldReg, newReg, category); - - addInterfence(newReg, oldReg); - - if (category == 2) { - addInterfence(newReg + 1, oldReg); - } - } - - /** - * Checks to see if old namespace reg {@code oldReg} interferes - * with what currently maps to {@code newReg}. - * - * @param oldReg old namespace register - * @param newReg new namespace register - * @param category category of old namespace register - * @return true if oldReg will interfere with newReg - */ - public boolean interferes(int oldReg, int newReg, int category) { - if (newReg >= newRegInterference.size()) { - return false; - } else { - IntSet existing = newRegInterference.get(newReg); - - if (existing == null) { - return false; - } else if (category == 1) { - return existing.has(oldReg); - } else { - return existing.has(oldReg) - || (interferes(oldReg, newReg+1, category-1)); - } - } + } + + /** + * Checks to see if old namespace reg {@code oldReg} interferes + * with what currently maps to {@code newReg}. + * + * @param oldReg old namespace register + * @param newReg new namespace register + * @param category category of old namespace register + * @return true if oldReg will interfere with newReg + */ + public boolean interferes(int oldReg, int newReg, int category) { + if (newReg >= newRegInterference.size()) { + return false; + } else { + IntSet existing = newRegInterference.get(newReg); + + if (existing == null) { + return false; + } else if (category == 1) { + return existing.has(oldReg); + } else { + return existing.has(oldReg) || (interferes(oldReg, newReg + 1, category - 1)); + } } - - /** - * Checks to see if old namespace reg {@code oldReg} interferes - * with what currently maps to {@code newReg}. - * - * @param oldSpec {@code non-null;} old namespace register - * @param newReg new namespace register - * @return true if oldReg will interfere with newReg - */ - public boolean interferes(RegisterSpec oldSpec, int newReg) { - return interferes(oldSpec.getReg(), newReg, oldSpec.getCategory()); + } + + /** + * Checks to see if old namespace reg {@code oldReg} interferes + * with what currently maps to {@code newReg}. + * + * @param oldSpec {@code non-null;} old namespace register + * @param newReg new namespace register + * @return true if oldReg will interfere with newReg + */ + public boolean interferes(RegisterSpec oldSpec, int newReg) { + return interferes(oldSpec.getReg(), newReg, oldSpec.getCategory()); + } + + /** + * Adds a register's interference set to the interference list, + * growing it if necessary. + * + * @param newReg register in new namespace + * @param oldReg register in old namespace + */ + private void addInterfence(int newReg, int oldReg) { + newRegInterference.ensureCapacity(newReg + 1); + + while (newReg >= newRegInterference.size()) { + newRegInterference.add(new BitIntSet(newReg + 1)); } - /** - * Adds a register's interference set to the interference list, - * growing it if necessary. - * - * @param newReg register in new namespace - * @param oldReg register in old namespace - */ - private void addInterfence(int newReg, int oldReg) { - newRegInterference.ensureCapacity(newReg + 1); - - while (newReg >= newRegInterference.size()) { - newRegInterference.add(new BitIntSet(newReg +1)); - } - - oldRegInterference.mergeInterferenceSet( - oldReg, newRegInterference.get(newReg)); + oldRegInterference.mergeInterferenceSet(oldReg, newRegInterference.get(newReg)); + } + + /** + * Checks to see if any of a set of old-namespace registers are + * pinned to the specified new-namespace reg + category. Takes into + * account the category of the old-namespace registers. + * + * @param oldSpecs {@code non-null;} set of old-namespace regs + * @param newReg {@code >= 0;} new-namespace register + * @param targetCategory {@code 1..2;} the number of adjacent new-namespace + * registers (starting at ropReg) to consider + * @return true if any of the old-namespace register have been mapped + * to the new-namespace register + category + */ + public boolean areAnyPinned(RegisterSpecList oldSpecs, int newReg, int targetCategory) { + int sz = oldSpecs.size(); + + for (int i = 0; i < sz; i++) { + RegisterSpec oldSpec = oldSpecs.get(i); + int r = oldToNew(oldSpec.getReg()); + + /* + * If oldSpec is a category-2 register, then check both newReg + * and newReg - 1. + */ + if (r == newReg || (oldSpec.getCategory() == 2 && (r + 1) == newReg) + || (targetCategory == 2 && (r == newReg + 1))) { + return true; + } } - /** - * Checks to see if any of a set of old-namespace registers are - * pinned to the specified new-namespace reg + category. Takes into - * account the category of the old-namespace registers. - * - * @param oldSpecs {@code non-null;} set of old-namespace regs - * @param newReg {@code >= 0;} new-namespace register - * @param targetCategory {@code 1..2;} the number of adjacent new-namespace - * registers (starting at ropReg) to consider - * @return true if any of the old-namespace register have been mapped - * to the new-namespace register + category - */ - public boolean areAnyPinned(RegisterSpecList oldSpecs, - int newReg, int targetCategory) { - int sz = oldSpecs.size(); - - for (int i = 0; i < sz; i++) { - RegisterSpec oldSpec = oldSpecs.get(i); - int r = oldToNew(oldSpec.getReg()); - - /* - * If oldSpec is a category-2 register, then check both newReg - * and newReg - 1. - */ - if (r == newReg - || (oldSpec.getCategory() == 2 && (r + 1) == newReg) - || (targetCategory == 2 && (r == newReg + 1))) { - return true; - } - } - - return false; - } + return false; + } } diff --git a/dx/src/com/android/jack/dx/ssa/LiteralOpUpgrader.java b/dx/src/com/android/jack/dx/ssa/LiteralOpUpgrader.java index ebf9401..5bbfdcb 100644 --- a/dx/src/com/android/jack/dx/ssa/LiteralOpUpgrader.java +++ b/dx/src/com/android/jack/dx/ssa/LiteralOpUpgrader.java @@ -40,168 +40,163 @@ import java.util.List; */ public class LiteralOpUpgrader { - /** method we're processing */ - private final SsaMethod ssaMeth; - - /** - * Process a method. - * - * @param ssaMethod {@code non-null;} method to process - */ - public static void process(SsaMethod ssaMethod) { - LiteralOpUpgrader dc; - - dc = new LiteralOpUpgrader(ssaMethod); - - dc.run(); + /** method we're processing */ + private final SsaMethod ssaMeth; + + /** + * Process a method. + * + * @param ssaMethod {@code non-null;} method to process + */ + public static void process(SsaMethod ssaMethod) { + LiteralOpUpgrader dc; + + dc = new LiteralOpUpgrader(ssaMethod); + + dc.run(); + } + + private LiteralOpUpgrader(SsaMethod ssaMethod) { + this.ssaMeth = ssaMethod; + } + + /** + * Returns true if the register contains an integer 0 or a known-null + * object reference + * + * @param spec non-null spec + * @return true for 0 or null type bearers + */ + private static boolean isConstIntZeroOrKnownNull(RegisterSpec spec) { + TypeBearer tb = spec.getTypeBearer(); + if (tb instanceof CstLiteralBits) { + CstLiteralBits clb = (CstLiteralBits) tb; + return (clb.getLongBits() == 0); } + return false; + } - private LiteralOpUpgrader(SsaMethod ssaMethod) { - this.ssaMeth = ssaMethod; - } + /** + * Run the literal op upgrader + */ + private void run() { + final TranslationAdvice advice = Optimizer.getAdvice(); - /** - * Returns true if the register contains an integer 0 or a known-null - * object reference - * - * @param spec non-null spec - * @return true for 0 or null type bearers - */ - private static boolean isConstIntZeroOrKnownNull(RegisterSpec spec) { - TypeBearer tb = spec.getTypeBearer(); - if (tb instanceof CstLiteralBits) { - CstLiteralBits clb = (CstLiteralBits) tb; - return (clb.getLongBits() == 0); - } - return false; - } + ssaMeth.forEachInsn(new SsaInsn.Visitor() { + @Override + public void visitMoveInsn(NormalSsaInsn insn) { + // do nothing + } - /** - * Run the literal op upgrader - */ - private void run() { - final TranslationAdvice advice = Optimizer.getAdvice(); - - ssaMeth.forEachInsn(new SsaInsn.Visitor() { - public void visitMoveInsn(NormalSsaInsn insn) { - // do nothing - } - - public void visitPhiInsn(PhiInsn insn) { - // do nothing - } - - public void visitNonMoveInsn(NormalSsaInsn insn) { - - Insn originalRopInsn = insn.getOriginalRopInsn(); - Rop opcode = originalRopInsn.getOpcode(); - RegisterSpecList sources = insn.getSources(); - - // Replace insns with constant results with const insns - if (tryReplacingWithConstant(insn)) return; - - if (sources.size() != 2 ) { - // We're only dealing with two-source insns here. - return; - } - - if (opcode.getBranchingness() == Rop.BRANCH_IF) { - /* - * An if instruction can become an if-*z instruction. - */ - if (isConstIntZeroOrKnownNull(sources.get(0))) { - replacePlainInsn(insn, sources.withoutFirst(), - RegOps.flippedIfOpcode(opcode.getOpcode()), null); - } else if (isConstIntZeroOrKnownNull(sources.get(1))) { - replacePlainInsn(insn, sources.withoutLast(), - opcode.getOpcode(), null); - } - } else if (advice.hasConstantOperation( - opcode, sources.get(0), sources.get(1))) { - insn.upgradeToLiteral(); - } else if (opcode.isCommutative() - && advice.hasConstantOperation( - opcode, sources.get(1), sources.get(0))) { - /* - * An instruction can be commuted to a literal operation - */ - - insn.setNewSources( - RegisterSpecList.make( - sources.get(1), sources.get(0))); - - insn.upgradeToLiteral(); - } - } - }); - } + @Override + public void visitPhiInsn(PhiInsn insn) { + // do nothing + } + + @Override + public void visitNonMoveInsn(NormalSsaInsn insn) { - /** - * Tries to replace an instruction with a const instruction. The given - * instruction must have a constant result for it to be replaced. - * - * @param insn {@code non-null;} instruction to try to replace - * @return true if the instruction was replaced - */ - private boolean tryReplacingWithConstant(NormalSsaInsn insn) { Insn originalRopInsn = insn.getOriginalRopInsn(); Rop opcode = originalRopInsn.getOpcode(); - RegisterSpec result = insn.getResult(); - - if (result != null && !ssaMeth.isRegALocal(result) && - opcode.getOpcode() != RegOps.CONST) { - TypeBearer type = insn.getResult().getTypeBearer(); - if (type.isConstant() && type.getBasicType() == Type.BT_INT) { - // Replace the instruction with a constant - replacePlainInsn(insn, RegisterSpecList.EMPTY, - RegOps.CONST, (Constant) type); - - // Remove the source as well if this is a move-result-pseudo - if (opcode.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { - int pred = insn.getBlock().getPredecessors().nextSetBit(0); - ArrayList<SsaInsn> predInsns = - ssaMeth.getBlocks().get(pred).getInsns(); - NormalSsaInsn sourceInsn = - (NormalSsaInsn) predInsns.get(predInsns.size()-1); - replacePlainInsn(sourceInsn, RegisterSpecList.EMPTY, - RegOps.GOTO, null); - } - return true; - } + RegisterSpecList sources = insn.getSources(); + + // Replace insns with constant results with const insns + if (tryReplacingWithConstant(insn)) { + return; } - return false; - } - /** - * Replaces an SsaInsn containing a PlainInsn with a new PlainInsn. The - * new PlainInsn is constructed with a new RegOp and new sources. - * - * TODO move this somewhere else. - * - * @param insn {@code non-null;} an SsaInsn containing a PlainInsn - * @param newSources {@code non-null;} new sources list for new insn - * @param newOpcode A RegOp from {@link RegOps} - * @param cst {@code null-ok;} constant for new instruction, if any - */ - private void replacePlainInsn(NormalSsaInsn insn, - RegisterSpecList newSources, int newOpcode, Constant cst) { + if (sources.size() != 2) { + // We're only dealing with two-source insns here. + return; + } - Insn originalRopInsn = insn.getOriginalRopInsn(); - Rop newRop = Rops.ropFor(newOpcode, insn.getResult(), newSources, cst); - Insn newRopInsn; - if (cst == null) { - newRopInsn = new PlainInsn(newRop, originalRopInsn.getPosition(), - insn.getResult(), newSources); - } else { - newRopInsn = new PlainCstInsn(newRop, originalRopInsn.getPosition(), - insn.getResult(), newSources, cst); + if (opcode.getBranchingness() == Rop.BRANCH_IF) { + /* + * An if instruction can become an if-*z instruction. + */ + if (isConstIntZeroOrKnownNull(sources.get(0))) { + replacePlainInsn(insn, sources.withoutFirst(), + RegOps.flippedIfOpcode(opcode.getOpcode()), null); + } else if (isConstIntZeroOrKnownNull(sources.get(1))) { + replacePlainInsn(insn, sources.withoutLast(), opcode.getOpcode(), null); + } + } else if (advice.hasConstantOperation(opcode, sources.get(0), sources.get(1))) { + insn.upgradeToLiteral(); + } else if (opcode.isCommutative() + && advice.hasConstantOperation(opcode, sources.get(1), sources.get(0))) { + /* + * An instruction can be commuted to a literal operation + */ + +insn.setNewSources(RegisterSpecList.make(sources.get(1), sources.get(0))); + + insn.upgradeToLiteral(); } - NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); + } + }); + } + + /** + * Tries to replace an instruction with a const instruction. The given + * instruction must have a constant result for it to be replaced. + * + * @param insn {@code non-null;} instruction to try to replace + * @return true if the instruction was replaced + */ + private boolean tryReplacingWithConstant(NormalSsaInsn insn) { + Insn originalRopInsn = insn.getOriginalRopInsn(); + Rop opcode = originalRopInsn.getOpcode(); + RegisterSpec result = insn.getResult(); + + if (result != null && !ssaMeth.isRegALocal(result) && opcode.getOpcode() != RegOps.CONST) { + TypeBearer type = insn.getResult().getTypeBearer(); + if (type.isConstant() && type.getBasicType() == Type.BT_INT) { + // Replace the instruction with a constant + replacePlainInsn(insn, RegisterSpecList.EMPTY, RegOps.CONST, (Constant) type); + + // Remove the source as well if this is a move-result-pseudo + if (opcode.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { + int pred = insn.getBlock().getPredecessors().nextSetBit(0); + ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns(); + NormalSsaInsn sourceInsn = (NormalSsaInsn) predInsns.get(predInsns.size() - 1); + replacePlainInsn(sourceInsn, RegisterSpecList.EMPTY, RegOps.GOTO, null); + } + return true; + } + } + return false; + } + + /** + * Replaces an SsaInsn containing a PlainInsn with a new PlainInsn. The + * new PlainInsn is constructed with a new RegOp and new sources. + * + * TODO(dx team) move this somewhere else. + * + * @param insn {@code non-null;} an SsaInsn containing a PlainInsn + * @param newSources {@code non-null;} new sources list for new insn + * @param newOpcode A RegOp from {@link RegOps} + * @param cst {@code null-ok;} constant for new instruction, if any + */ + private void replacePlainInsn(NormalSsaInsn insn, RegisterSpecList newSources, int newOpcode, + Constant cst) { + + Insn originalRopInsn = insn.getOriginalRopInsn(); + Rop newRop = Rops.ropFor(newOpcode, insn.getResult(), newSources, cst); + Insn newRopInsn; + if (cst == null) { + newRopInsn = + new PlainInsn(newRop, originalRopInsn.getPosition(), insn.getResult(), newSources); + } else { + newRopInsn = new PlainCstInsn(newRop, originalRopInsn.getPosition(), insn.getResult(), + newSources, cst); + } + NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); - List<SsaInsn> insns = insn.getBlock().getInsns(); + List<SsaInsn> insns = insn.getBlock().getInsns(); - ssaMeth.onInsnRemoved(insn); - insns.set(insns.lastIndexOf(insn), newInsn); - ssaMeth.onInsnAdded(newInsn); - } + ssaMeth.onInsnRemoved(insn); + insns.set(insns.lastIndexOf(insn), newInsn); + ssaMeth.onInsnAdded(newInsn); + } } diff --git a/dx/src/com/android/jack/dx/ssa/LocalVariableExtractor.java b/dx/src/com/android/jack/dx/ssa/LocalVariableExtractor.java index a16eeb1..38490ae 100644 --- a/dx/src/com/android/jack/dx/ssa/LocalVariableExtractor.java +++ b/dx/src/com/android/jack/dx/ssa/LocalVariableExtractor.java @@ -29,180 +29,172 @@ import java.util.List; * a method. Stolen and retrofitted from * com.android.jack.dx.rop.code.LocalVariableExtractor * - * TODO remove this. Allow Rop-form LocalVariableInfo to be passed in, + * TODO(dx team) remove this. Allow Rop-form LocalVariableInfo to be passed in, * converted, and adapted through edge-splitting. */ public class LocalVariableExtractor { - /** {@code non-null;} method being extracted from */ - private final SsaMethod method; - - /** {@code non-null;} block list for the method */ - private final ArrayList<SsaBasicBlock> blocks; - - /** {@code non-null;} result in-progress */ - private final LocalVariableInfo resultInfo; + /** {@code non-null;} method being extracted from */ + private final SsaMethod method; + + /** {@code non-null;} block list for the method */ + private final ArrayList<SsaBasicBlock> blocks; + + /** {@code non-null;} result in-progress */ + private final LocalVariableInfo resultInfo; + + /** {@code non-null;} work set indicating blocks needing to be processed */ + private final BitSet workSet; + + /** + * Extracts out all the local variable information from the given method. + * + * @param method {@code non-null;} the method to extract from + * @return {@code non-null;} the extracted information + */ + public static LocalVariableInfo extract(SsaMethod method) { + LocalVariableExtractor lve = new LocalVariableExtractor(method); + return lve.doit(); + } + + /** + * Constructs an instance. This method is private. Use {@link #extract}. + * + * @param method {@code non-null;} the method to extract from + */ + private LocalVariableExtractor(SsaMethod method) { + if (method == null) { + throw new NullPointerException("method == null"); + } - /** {@code non-null;} work set indicating blocks needing to be processed */ - private final BitSet workSet; + ArrayList<SsaBasicBlock> blocks = method.getBlocks(); + + this.method = method; + this.blocks = blocks; + this.resultInfo = new LocalVariableInfo(method); + this.workSet = new BitSet(blocks.size()); + } + + /** + * Does the extraction. + * + * @return {@code non-null;} the extracted information + */ + private LocalVariableInfo doit() { + + //TODO(dx team) why is this needed here? + if (method.getRegCount() > 0) { + for (int bi = method.getEntryBlockIndex(); bi >= 0; bi = workSet.nextSetBit(0)) { + workSet.clear(bi); + processBlock(bi); + } + } - /** - * Extracts out all the local variable information from the given method. - * - * @param method {@code non-null;} the method to extract from - * @return {@code non-null;} the extracted information - */ - public static LocalVariableInfo extract(SsaMethod method) { - LocalVariableExtractor lve = new LocalVariableExtractor(method); - return lve.doit(); + resultInfo.setImmutable(); + return resultInfo; + } + + /** + * Processes a single block. + * + * @param blockIndex {@code >= 0;} block index of the block to process + */ + private void processBlock(int blockIndex) { + RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(blockIndex); + SsaBasicBlock block = blocks.get(blockIndex); + List<SsaInsn> insns = block.getInsns(); + int insnSz = insns.size(); + + // The exit block has no insns and no successors + if (blockIndex == method.getExitBlockIndex()) { + return; } - /** - * Constructs an instance. This method is private. Use {@link #extract}. - * - * @param method {@code non-null;} the method to extract from + /* + * We may have to treat the last instruction specially: If it + * can (but doesn't always) throw, and the exception can be + * caught within the same method, then we need to use the + * state *before* executing it to be what is merged into + * exception targets. + */ + SsaInsn lastInsn = insns.get(insnSz - 1); + boolean hasExceptionHandlers = lastInsn.getOriginalRopInsn().getCatches().size() != 0; + boolean canThrowDuringLastInsn = hasExceptionHandlers && (lastInsn.getResult() != null); + int freezeSecondaryStateAt = insnSz - 1; + RegisterSpecSet secondaryState = primaryState; + + /* + * Iterate over the instructions, adding information for each place + * that the active variable set changes. */ - private LocalVariableExtractor(SsaMethod method) { - if (method == null) { - throw new NullPointerException("method == null"); - } - ArrayList<SsaBasicBlock> blocks = method.getBlocks(); +for (int i = 0; i < insnSz; i++) { + if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) { + // Until this point, primaryState == secondaryState. + primaryState.setImmutable(); + primaryState = primaryState.mutableCopy(); + } - this.method = method; - this.blocks = blocks; - this.resultInfo = new LocalVariableInfo(method); - this.workSet = new BitSet(blocks.size()); - } + SsaInsn insn = insns.get(i); + RegisterSpec result; - /** - * Does the extraction. - * - * @return {@code non-null;} the extracted information - */ - private LocalVariableInfo doit() { - - //FIXME why is this needed here? - if (method.getRegCount() > 0 ) { - for (int bi = method.getEntryBlockIndex(); - bi >= 0; - bi = workSet.nextSetBit(0)) { - workSet.clear(bi); - processBlock(bi); - } - } + result = insn.getLocalAssignment(); - resultInfo.setImmutable(); - return resultInfo; - } + if (result == null) { + // We may be nuking an existing local - /** - * Processes a single block. - * - * @param blockIndex {@code >= 0;} block index of the block to process - */ - private void processBlock(int blockIndex) { - RegisterSpecSet primaryState - = resultInfo.mutableCopyOfStarts(blockIndex); - SsaBasicBlock block = blocks.get(blockIndex); - List<SsaInsn> insns = block.getInsns(); - int insnSz = insns.size(); - - // The exit block has no insns and no successors - if (blockIndex == method.getExitBlockIndex()) { - return; - } + result = insn.getResult(); + if (result != null && primaryState.get(result.getReg()) != null) { + primaryState.remove(primaryState.get(result.getReg())); + } + continue; + } + + result = result.withSimpleType(); + + RegisterSpec already = primaryState.get(result); + /* + * The equals() check ensures we only add new info if + * the instruction causes a change to the set of + * active variables. + */ + if (!result.equals(already)) { /* - * We may have to treat the last instruction specially: If it - * can (but doesn't always) throw, and the exception can be - * caught within the same method, then we need to use the - * state *before* executing it to be what is merged into - * exception targets. + * If this insn represents a local moving from one register + * to another, remove the association between the old register + * and the local. */ - SsaInsn lastInsn = insns.get(insnSz - 1); - boolean hasExceptionHandlers - = lastInsn.getOriginalRopInsn().getCatches().size() !=0 ; - boolean canThrowDuringLastInsn = hasExceptionHandlers - && (lastInsn.getResult() != null); - int freezeSecondaryStateAt = insnSz - 1; - RegisterSpecSet secondaryState = primaryState; + RegisterSpec previous = primaryState.localItemToSpec(result.getLocalItem()); - /* - * Iterate over the instructions, adding information for each place - * that the active variable set changes. - */ + if (previous != null && (previous.getReg() != result.getReg())) { - for (int i = 0; i < insnSz; i++) { - if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) { - // Until this point, primaryState == secondaryState. - primaryState.setImmutable(); - primaryState = primaryState.mutableCopy(); - } - - SsaInsn insn = insns.get(i); - RegisterSpec result; - - result = insn.getLocalAssignment(); - - if (result == null) { - // We may be nuking an existing local - - result = insn.getResult(); - - if (result != null && primaryState.get(result.getReg()) != null) { - primaryState.remove(primaryState.get(result.getReg())); - } - continue; - } - - result = result.withSimpleType(); - - RegisterSpec already = primaryState.get(result); - /* - * The equals() check ensures we only add new info if - * the instruction causes a change to the set of - * active variables. - */ - if (!result.equals(already)) { - /* - * If this insn represents a local moving from one register - * to another, remove the association between the old register - * and the local. - */ - RegisterSpec previous - = primaryState.localItemToSpec(result.getLocalItem()); - - if (previous != null - && (previous.getReg() != result.getReg())) { - - primaryState.remove(previous); - } - - resultInfo.addAssignment(insn, result); - primaryState.put(result); - } + primaryState.remove(previous); } - primaryState.setImmutable(); + resultInfo.addAssignment(insn, result); + primaryState.put(result); + } + } - /* - * Merge this state into the start state for each successor, - * and update the work set where required (that is, in cases - * where the start state for a block changes). - */ + primaryState.setImmutable(); - IntList successors = block.getSuccessorList(); - int succSz = successors.size(); - int primarySuccessor = block.getPrimarySuccessorIndex(); + /* + * Merge this state into the start state for each successor, + * and update the work set where required (that is, in cases + * where the start state for a block changes). + */ - for (int i = 0; i < succSz; i++) { - int succ = successors.get(i); - RegisterSpecSet state = (succ == primarySuccessor) ? - primaryState : secondaryState; +IntList successors = block.getSuccessorList(); + int succSz = successors.size(); + int primarySuccessor = block.getPrimarySuccessorIndex(); - if (resultInfo.mergeStarts(succ, state)) { - workSet.set(succ); - } - } + for (int i = 0; i < succSz; i++) { + int succ = successors.get(i); + RegisterSpecSet state = (succ == primarySuccessor) ? primaryState : secondaryState; + + if (resultInfo.mergeStarts(succ, state)) { + workSet.set(succ); + } } + } } diff --git a/dx/src/com/android/jack/dx/ssa/LocalVariableInfo.java b/dx/src/com/android/jack/dx/ssa/LocalVariableInfo.java index 0ff957c..8bc7634 100644 --- a/dx/src/com/android/jack/dx/ssa/LocalVariableInfo.java +++ b/dx/src/com/android/jack/dx/ssa/LocalVariableInfo.java @@ -28,224 +28,221 @@ import java.util.List; * com.android.jack.dx.ssa.SsaMethod}. * Stolen from {@link com.android.jack.dx.rop.code.LocalVariableInfo}. */ -public class LocalVariableInfo extends MutabilityControl { - /** {@code >= 0;} the register count for the method */ - private final int regCount; - - /** - * {@code non-null;} {@link com.android.jack.dx.rop.code.RegisterSpecSet} to use when indicating a block - * that has no locals; it is empty and immutable but has an appropriate - * max size for the method - */ - private final RegisterSpecSet emptySet; - - /** - * {@code non-null;} array consisting of register sets representing the - * sets of variables already assigned upon entry to each block, - * where array indices correspond to block indices - */ - private final RegisterSpecSet[] blockStarts; - - /** {@code non-null;} map from instructions to the variable each assigns */ - private final HashMap<SsaInsn, RegisterSpec> insnAssignments; - - /** - * Constructs an instance. - * - * @param method {@code non-null;} the method being represented by this instance - */ - public LocalVariableInfo(SsaMethod method) { - if (method == null) { - throw new NullPointerException("method == null"); - } - - List<SsaBasicBlock> blocks = method.getBlocks(); - - this.regCount = method.getRegCount(); - this.emptySet = new RegisterSpecSet(regCount); - this.blockStarts = new RegisterSpecSet[blocks.size()]; - this.insnAssignments = - new HashMap<SsaInsn, RegisterSpec>(/*hint here*/); - - emptySet.setImmutable(); +public class LocalVariableInfo extends MutabilityControl { + /** {@code >= 0;} the register count for the method */ + private final int regCount; + + /** + * {@code non-null;} {@link com.android.jack.dx.rop.code.RegisterSpecSet} to use when indicating a + * block that has no locals; it is empty and immutable but has an appropriate max size for the + * method + */ + private final RegisterSpecSet emptySet; + + /** + * {@code non-null;} array consisting of register sets representing the + * sets of variables already assigned upon entry to each block, + * where array indices correspond to block indices + */ + private final RegisterSpecSet[] blockStarts; + + /** {@code non-null;} map from instructions to the variable each assigns */ + private final HashMap<SsaInsn, RegisterSpec> insnAssignments; + + /** + * Constructs an instance. + * + * @param method {@code non-null;} the method being represented by this instance + */ + public LocalVariableInfo(SsaMethod method) { + if (method == null) { + throw new NullPointerException("method == null"); } - /** - * Sets the register set associated with the start of the block with - * the given index. - * - * @param index {@code >= 0;} the block index - * @param specs {@code non-null;} the register set to associate with the block - */ - public void setStarts(int index, RegisterSpecSet specs) { - throwIfImmutable(); - - if (specs == null) { - throw new NullPointerException("specs == null"); - } - - try { - blockStarts[index] = specs; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("bogus index"); - } - } + List<SsaBasicBlock> blocks = method.getBlocks(); - /** - * Merges the given register set into the set for the block with the - * given index. If there was not already an associated set, then this - * is the same as calling {@link #setStarts}. Otherwise, this will - * merge the two sets and call {@link #setStarts} on the result of the - * merge. - * - * @param index {@code >= 0;} the block index - * @param specs {@code non-null;} the register set to merge into the start set - * for the block - * @return {@code true} if the merge resulted in an actual change - * to the associated set (including storing one for the first time) or - * {@code false} if there was no change - */ - public boolean mergeStarts(int index, RegisterSpecSet specs) { - RegisterSpecSet start = getStarts0(index); - boolean changed = false; - - if (start == null) { - setStarts(index, specs); - return true; - } - - RegisterSpecSet newStart = start.mutableCopy(); - newStart.intersect(specs, true); - - if (start.equals(newStart)) { - return false; - } - - newStart.setImmutable(); - setStarts(index, newStart); - - return true; - } + this.regCount = method.getRegCount(); + this.emptySet = new RegisterSpecSet(regCount); + this.blockStarts = new RegisterSpecSet[blocks.size()]; + this.insnAssignments = new HashMap<SsaInsn, RegisterSpec>(/*hint here*/); - /** - * Gets the register set associated with the start of the block - * with the given index. This returns an empty set with the appropriate - * max size if no set was associated with the block in question. - * - * @param index {@code >= 0;} the block index - * @return {@code non-null;} the associated register set - */ - public RegisterSpecSet getStarts(int index) { - RegisterSpecSet result = getStarts0(index); - - return (result != null) ? result : emptySet; - } + emptySet.setImmutable(); + } - /** - * Gets the register set associated with the start of the given - * block. This is just convenient shorthand for - * {@code getStarts(block.getLabel())}. - * - * @param block {@code non-null;} the block in question - * @return {@code non-null;} the associated register set - */ - public RegisterSpecSet getStarts(SsaBasicBlock block) { - return getStarts(block.getIndex()); - } + /** + * Sets the register set associated with the start of the block with + * the given index. + * + * @param index {@code >= 0;} the block index + * @param specs {@code non-null;} the register set to associate with the block + */ + public void setStarts(int index, RegisterSpecSet specs) { + throwIfImmutable(); - /** - * Gets a mutable copy of the register set associated with the - * start of the block with the given index. This returns a - * newly-allocated empty {@link RegisterSpecSet} of appropriate - * max size if there is not yet any set associated with the block. - * - * @param index {@code >= 0;} the block index - * @return {@code non-null;} the associated register set - */ - public RegisterSpecSet mutableCopyOfStarts(int index) { - RegisterSpecSet result = getStarts0(index); - - return (result != null) ? - result.mutableCopy() : new RegisterSpecSet(regCount); + if (specs == null) { + throw new NullPointerException("specs == null"); } - /** - * Adds an assignment association for the given instruction and - * register spec. This throws an exception if the instruction - * doesn't actually perform a named variable assignment. - * - * <b>Note:</b> Although the instruction contains its own spec for - * the result, it still needs to be passed in explicitly to this - * method, since the spec that is stored here should always have a - * simple type and the one in the instruction can be an arbitrary - * {@link com.android.jack.dx.rop.type.TypeBearer} (such as a constant value). - * - * @param insn {@code non-null;} the instruction in question - * @param spec {@code non-null;} the associated register spec - */ - public void addAssignment(SsaInsn insn, RegisterSpec spec) { - throwIfImmutable(); - - if (insn == null) { - throw new NullPointerException("insn == null"); - } - - if (spec == null) { - throw new NullPointerException("spec == null"); - } - - insnAssignments.put(insn, spec); + try { + blockStarts[index] = specs; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("bogus index"); } + } + + /** + * Merges the given register set into the set for the block with the + * given index. If there was not already an associated set, then this + * is the same as calling {@link #setStarts}. Otherwise, this will + * merge the two sets and call {@link #setStarts} on the result of the + * merge. + * + * @param index {@code >= 0;} the block index + * @param specs {@code non-null;} the register set to merge into the start set + * for the block + * @return {@code true} if the merge resulted in an actual change + * to the associated set (including storing one for the first time) or + * {@code false} if there was no change + */ + public boolean mergeStarts(int index, RegisterSpecSet specs) { + RegisterSpecSet start = getStarts0(index); + + if (start == null) { + setStarts(index, specs); + return true; + } + + RegisterSpecSet newStart = start.mutableCopy(); + newStart.intersect(specs, true); - /** - * Gets the named register being assigned by the given instruction, if - * previously stored in this instance. - * - * @param insn {@code non-null;} instruction in question - * @return {@code null-ok;} the named register being assigned, if any - */ - public RegisterSpec getAssignment(SsaInsn insn) { - return insnAssignments.get(insn); + if (start.equals(newStart)) { + return false; } - /** - * Gets the number of assignments recorded by this instance. - * - * @return {@code >= 0;} the number of assignments - */ - public int getAssignmentCount() { - return insnAssignments.size(); + newStart.setImmutable(); + setStarts(index, newStart); + + return true; + } + + /** + * Gets the register set associated with the start of the block + * with the given index. This returns an empty set with the appropriate + * max size if no set was associated with the block in question. + * + * @param index {@code >= 0;} the block index + * @return {@code non-null;} the associated register set + */ + public RegisterSpecSet getStarts(int index) { + RegisterSpecSet result = getStarts0(index); + + return (result != null) ? result : emptySet; + } + + /** + * Gets the register set associated with the start of the given + * block. This is just convenient shorthand for + * {@code getStarts(block.getLabel())}. + * + * @param block {@code non-null;} the block in question + * @return {@code non-null;} the associated register set + */ + public RegisterSpecSet getStarts(SsaBasicBlock block) { + return getStarts(block.getIndex()); + } + + /** + * Gets a mutable copy of the register set associated with the + * start of the block with the given index. This returns a + * newly-allocated empty {@link RegisterSpecSet} of appropriate + * max size if there is not yet any set associated with the block. + * + * @param index {@code >= 0;} the block index + * @return {@code non-null;} the associated register set + */ + public RegisterSpecSet mutableCopyOfStarts(int index) { + RegisterSpecSet result = getStarts0(index); + + return (result != null) ? result.mutableCopy() : new RegisterSpecSet(regCount); + } + + /** + * Adds an assignment association for the given instruction and + * register spec. This throws an exception if the instruction + * doesn't actually perform a named variable assignment. + * + * <b>Note:</b> Although the instruction contains its own spec for + * the result, it still needs to be passed in explicitly to this + * method, since the spec that is stored here should always have a + * simple type and the one in the instruction can be an arbitrary + * {@link com.android.jack.dx.rop.type.TypeBearer} (such as a constant value). + * + * @param insn {@code non-null;} the instruction in question + * @param spec {@code non-null;} the associated register spec + */ + public void addAssignment(SsaInsn insn, RegisterSpec spec) { + throwIfImmutable(); + + if (insn == null) { + throw new NullPointerException("insn == null"); } - public void debugDump() { - for (int index = 0 ; index < blockStarts.length; index++) { - if (blockStarts[index] == null) { - continue; - } - - if (blockStarts[index] == emptySet) { - System.out.printf("%04x: empty set\n", index); - } else { - System.out.printf("%04x: %s\n", index, blockStarts[index]); - } - } + if (spec == null) { + throw new NullPointerException("spec == null"); } - /** - * Helper method, to get the starts for a index, throwing the - * right exception for range problems. - * - * @param index {@code >= 0;} the block index - * @return {@code null-ok;} associated register set or {@code null} if there - * is none - */ - private RegisterSpecSet getStarts0(int index) { - try { - return blockStarts[index]; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - throw new IllegalArgumentException("bogus index"); - } + insnAssignments.put(insn, spec); + } + + /** + * Gets the named register being assigned by the given instruction, if + * previously stored in this instance. + * + * @param insn {@code non-null;} instruction in question + * @return {@code null-ok;} the named register being assigned, if any + */ + public RegisterSpec getAssignment(SsaInsn insn) { + return insnAssignments.get(insn); + } + + /** + * Gets the number of assignments recorded by this instance. + * + * @return {@code >= 0;} the number of assignments + */ + public int getAssignmentCount() { + return insnAssignments.size(); + } + + public void debugDump() { + for (int index = 0; index < blockStarts.length; index++) { + if (blockStarts[index] == null) { + continue; + } + + if (blockStarts[index] == emptySet) { + System.out.printf("%04x: empty set\n", index); + } else { + System.out.printf("%04x: %s\n", index, blockStarts[index]); + } + } + } + + /** + * Helper method, to get the starts for a index, throwing the + * right exception for range problems. + * + * @param index {@code >= 0;} the block index + * @return {@code null-ok;} associated register set or {@code null} if there + * is none + */ + private RegisterSpecSet getStarts0(int index) { + try { + return blockStarts[index]; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + throw new IllegalArgumentException("bogus index"); } + } } diff --git a/dx/src/com/android/jack/dx/ssa/MoveParamCombiner.java b/dx/src/com/android/jack/dx/ssa/MoveParamCombiner.java index dc05a6e..95fc869 100644 --- a/dx/src/com/android/jack/dx/ssa/MoveParamCombiner.java +++ b/dx/src/com/android/jack/dx/ssa/MoveParamCombiner.java @@ -23,7 +23,6 @@ import com.android.jack.dx.rop.code.RegisterSpec; import com.android.jack.dx.rop.cst.CstInteger; import java.util.HashSet; -import java.util.ArrayList; import java.util.List; /** @@ -32,125 +31,127 @@ import java.util.List; */ public class MoveParamCombiner { - /** method to process */ - private final SsaMethod ssaMeth; - - /** - * Processes a method with this optimization step. - * - * @param ssaMethod method to process - */ - public static void process(SsaMethod ssaMethod) { - new MoveParamCombiner(ssaMethod).run(); - } - - private MoveParamCombiner(SsaMethod ssaMeth) { - this.ssaMeth = ssaMeth; - } - - /** - * Runs this optimization step. - */ - private void run() { - // This will contain the definition specs for each parameter - final RegisterSpec[] paramSpecs - = new RegisterSpec[ssaMeth.getParamWidth()]; - - // Insns to delete when all done - final HashSet<SsaInsn> deletedInsns = new HashSet(); - - ssaMeth.forEachInsn(new SsaInsn.Visitor() { - public void visitMoveInsn (NormalSsaInsn insn) { + /** method to process */ + private final SsaMethod ssaMeth; + + /** + * Processes a method with this optimization step. + * + * @param ssaMethod method to process + */ + public static void process(SsaMethod ssaMethod) { + new MoveParamCombiner(ssaMethod).run(); + } + + private MoveParamCombiner(SsaMethod ssaMeth) { + this.ssaMeth = ssaMeth; + } + + /** + * Runs this optimization step. + */ + private void run() { + // This will contain the definition specs for each parameter + final RegisterSpec[] paramSpecs = new RegisterSpec[ssaMeth.getParamWidth()]; + + // Insns to delete when all done + final HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>(); + + ssaMeth.forEachInsn(new SsaInsn.Visitor() { + @Override + public void visitMoveInsn(NormalSsaInsn insn) {} + + @Override + public void visitPhiInsn(PhiInsn phi) {} + + @Override + public void visitNonMoveInsn(NormalSsaInsn insn) { + if (insn.getOpcode().getOpcode() != RegOps.MOVE_PARAM) { + return; + } + + int param = getParamIndex(insn); + + if (paramSpecs[param] == null) { + paramSpecs[param] = insn.getResult(); + } else { + final RegisterSpec specA = paramSpecs[param]; + final RegisterSpec specB = insn.getResult(); + LocalItem localA = specA.getLocalItem(); + LocalItem localB = specB.getLocalItem(); + LocalItem newLocal; + + /* + * Is there local information to preserve? + */ + +if (localA == null) { + newLocal = localB; + } else if (localB == null) { + newLocal = localA; + } else if (localA.equals(localB)) { + newLocal = localA; + } else { + /* + * Oddly, these two identical move-params have distinct + * debug info. We'll just keep them distinct. + */ + return; + } + + ssaMeth.getDefinitionForRegister(specA.getReg()).setResultLocal(newLocal); + + /* + * Map all uses of specB to specA + */ + +RegisterMapper mapper = new RegisterMapper() { + /** @inheritDoc */ + @Override + public int getNewRegisterCount() { + return ssaMeth.getRegCount(); } - public void visitPhiInsn (PhiInsn phi) { - } - public void visitNonMoveInsn (NormalSsaInsn insn) { - if (insn.getOpcode().getOpcode() != RegOps.MOVE_PARAM) { - return; - } - - int param = getParamIndex(insn); - - if (paramSpecs[param] == null) { - paramSpecs[param] = insn.getResult(); - } else { - final RegisterSpec specA = paramSpecs[param]; - final RegisterSpec specB = insn.getResult(); - LocalItem localA = specA.getLocalItem(); - LocalItem localB = specB.getLocalItem(); - LocalItem newLocal; - - /* - * Is there local information to preserve? - */ - - if (localA == null) { - newLocal = localB; - } else if (localB == null) { - newLocal = localA; - } else if (localA.equals(localB)) { - newLocal = localA; - } else { - /* - * Oddly, these two identical move-params have distinct - * debug info. We'll just keep them distinct. - */ - return; - } - - ssaMeth.getDefinitionForRegister(specA.getReg()) - .setResultLocal(newLocal); - - /* - * Map all uses of specB to specA - */ - - RegisterMapper mapper = new RegisterMapper() { - /** @inheritDoc */ - public int getNewRegisterCount() { - return ssaMeth.getRegCount(); - } - - /** @inheritDoc */ - public RegisterSpec map(RegisterSpec registerSpec) { - if (registerSpec.getReg() == specB.getReg()) { - return specA; - } - - return registerSpec; - } - }; - - List<SsaInsn> uses - = ssaMeth.getUseListForRegister(specB.getReg()); - - // Use list is modified by mapSourceRegisters - for (int i = uses.size() - 1; i >= 0; i--) { - SsaInsn use = uses.get(i); - use.mapSourceRegisters(mapper); - } - - deletedInsns.add(insn); - } + /** @inheritDoc */ + @Override + public RegisterSpec map(RegisterSpec registerSpec) { + if (registerSpec.getReg() == specB.getReg()) { + return specA; + } + + return registerSpec; } - }); - - ssaMeth.deleteInsns(deletedInsns); - } - - /** - * Returns the parameter index associated with a move-param insn. Does - * not verify that the insn is a move-param insn. - * - * @param insn {@code non-null;} a move-param insn - * @return {@code >=0;} parameter index - */ - private int getParamIndex(NormalSsaInsn insn) { - CstInsn cstInsn = (CstInsn)(insn.getOriginalRopInsn()); - - int param = ((CstInteger)cstInsn.getConstant()).getValue(); - return param; - } + }; + + List<SsaInsn> uses = ssaMeth.getUseListForRegister(specB.getReg()); + + // Use list is modified by mapSourceRegisters + for (int i = uses.size() - 1; i >= 0; i--) { + SsaInsn use = uses.get(i); + use.mapSourceRegisters(mapper); + } + + deletedInsns.add(insn); + } + + } + }); + + ssaMeth.deleteInsns(deletedInsns); + } + + /** + * Returns the parameter index associated with a move-param insn. Does + * not verify that the insn is a move-param insn. + * + * @param insn {@code non-null;} a move-param insn + * @return {@code >=0;} parameter index + */ + private int getParamIndex(NormalSsaInsn insn) { + CstInsn cstInsn = (CstInsn) (insn.getOriginalRopInsn()); + + int param = ((CstInteger) cstInsn.getConstant()).getValue(); + return param; + } } diff --git a/dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java b/dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java index c669566..b62e554 100644 --- a/dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java +++ b/dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java @@ -16,221 +16,226 @@ package com.android.jack.dx.ssa; -import com.android.jack.dx.rop.code.*; +import com.android.jack.dx.rop.code.Insn; +import com.android.jack.dx.rop.code.LocalItem; +import com.android.jack.dx.rop.code.RegOps; +import com.android.jack.dx.rop.code.RegisterSpec; +import com.android.jack.dx.rop.code.RegisterSpecList; +import com.android.jack.dx.rop.code.Rop; /** * A "normal" (non-phi) instruction in SSA form. Always wraps a rop insn. */ public final class NormalSsaInsn extends SsaInsn implements Cloneable { - /** {@code non-null;} rop insn that we're wrapping */ - private Insn insn; - - /** - * Creates an instance. - * - * @param insn Rop insn to wrap - * @param block block that contains this insn - */ - NormalSsaInsn(final Insn insn, final SsaBasicBlock block) { - super(insn.getResult(), block); - this.insn = insn; - } - - /** {@inheritDoc} */ - @Override - public final void mapSourceRegisters(RegisterMapper mapper) { - RegisterSpecList oldSources = insn.getSources(); - RegisterSpecList newSources = mapper.map(oldSources); - - if (newSources != oldSources) { - insn = insn.withNewRegisters(getResult(), newSources); - getBlock().getParent().onSourcesChanged(this, oldSources); - } - } - - /** - * Changes one of the insn's sources. New source should be of same type - * and category. - * - * @param index {@code >=0;} index of source to change - * @param newSpec spec for new source - */ - public final void changeOneSource(int index, RegisterSpec newSpec) { - RegisterSpecList origSources = insn.getSources(); - int sz = origSources.size(); - RegisterSpecList newSources = new RegisterSpecList(sz); - - for (int i = 0; i < sz; i++) { - newSources.set(i, i == index ? newSpec : origSources.get(i)); - } - - newSources.setImmutable(); - - RegisterSpec origSpec = origSources.get(index); - if (origSpec.getReg() != newSpec.getReg()) { - /* - * If the register remains unchanged, we're only changing - * the type or local var name so don't update use list - */ - getBlock().getParent().onSourceChanged(this, origSpec, newSpec); - } - - insn = insn.withNewRegisters(getResult(), newSources); - } - - /** - * Changes the source list of the insn. New source list should be the - * same size and consist of sources of identical types. - * - * @param newSources non-null new sources list. - */ - public final void setNewSources (RegisterSpecList newSources) { - RegisterSpecList origSources = insn.getSources(); - - if (origSources.size() != newSources.size()) { - throw new RuntimeException("Sources counts don't match"); - } - - insn = insn.withNewRegisters(getResult(), newSources); - } - - /** {@inheritDoc} */ - @Override - public NormalSsaInsn clone() { - return (NormalSsaInsn) super.clone(); - } - - /** - * Like rop.Insn.getSources(). - * - * @return {@code null-ok;} sources list - */ - @Override - public RegisterSpecList getSources() { - return insn.getSources(); - } - - /** {@inheritDoc} */ - public String toHuman() { - return toRopInsn().toHuman(); - } - - /** {@inheritDoc} */ - @Override - public Insn toRopInsn() { - return insn.withNewRegisters(getResult(), insn.getSources()); - } - - /** - * @return the Rop opcode for this insn - */ - @Override - public Rop getOpcode() { - return insn.getOpcode(); - } - - /** {@inheritDoc} */ - @Override - public Insn getOriginalRopInsn() { - return insn; - } - - /** {@inheritDoc} */ - @Override - public RegisterSpec getLocalAssignment() { - RegisterSpec assignment; - - if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) { - assignment = insn.getSources().get(0); - } else { - assignment = getResult(); - } - - if (assignment == null) { - return null; - } - - LocalItem local = assignment.getLocalItem(); - - if (local == null) { - return null; - } - - return assignment; - } - - /** - * Upgrades this insn to a version that represents the constant source - * literally. If the upgrade is not possible, this does nothing. - * - * @see Insn#withSourceLiteral - */ - public void upgradeToLiteral() { - RegisterSpecList oldSources = insn.getSources(); - - insn = insn.withSourceLiteral(); - getBlock().getParent().onSourcesChanged(this, oldSources); - } - - /** - * @return true if this is a move (but not a move-operand) instruction - */ - @Override - public boolean isNormalMoveInsn() { - return insn.getOpcode().getOpcode() == RegOps.MOVE; - } - - /** {@inheritDoc} */ - @Override - public boolean isMoveException() { - return insn.getOpcode().getOpcode() == RegOps.MOVE_EXCEPTION; - } - - /** {@inheritDoc} */ - @Override - public boolean canThrow() { - return insn.canThrow(); - } - - /** {@inheritDoc} */ - @Override - public void accept(Visitor v) { - if (isNormalMoveInsn()) { - v.visitMoveInsn(this); - } else { - v.visitNonMoveInsn(this); - } - } - - /** {@inheritDoc} */ - @Override - public boolean isPhiOrMove() { - return isNormalMoveInsn(); - } - - /** - * {@inheritDoc} - * - * TODO: Increase the scope of this. - */ - @Override - public boolean hasSideEffect() { - Rop opcode = getOpcode(); - - if (opcode.getBranchingness() != Rop.BRANCH_NONE) { - return true; - } - - boolean hasLocalSideEffect - = Optimizer.getPreserveLocals() && getLocalAssignment() != null; - - switch (opcode.getOpcode()) { - case RegOps.MOVE_RESULT: - case RegOps.MOVE: - case RegOps.CONST: - return hasLocalSideEffect; - default: - return true; - } - } + /** {@code non-null;} rop insn that we're wrapping */ + private Insn insn; + + /** + * Creates an instance. + * + * @param insn Rop insn to wrap + * @param block block that contains this insn + */ + NormalSsaInsn(final Insn insn, final SsaBasicBlock block) { + super(insn.getResult(), block); + this.insn = insn; + } + + /** {@inheritDoc} */ + @Override + public final void mapSourceRegisters(RegisterMapper mapper) { + RegisterSpecList oldSources = insn.getSources(); + RegisterSpecList newSources = mapper.map(oldSources); + + if (newSources != oldSources) { + insn = insn.withNewRegisters(getResult(), newSources); + getBlock().getParent().onSourcesChanged(this, oldSources); + } + } + + /** + * Changes one of the insn's sources. New source should be of same type + * and category. + * + * @param index {@code >=0;} index of source to change + * @param newSpec spec for new source + */ + public final void changeOneSource(int index, RegisterSpec newSpec) { + RegisterSpecList origSources = insn.getSources(); + int sz = origSources.size(); + RegisterSpecList newSources = new RegisterSpecList(sz); + + for (int i = 0; i < sz; i++) { + newSources.set(i, i == index ? newSpec : origSources.get(i)); + } + + newSources.setImmutable(); + + RegisterSpec origSpec = origSources.get(index); + if (origSpec.getReg() != newSpec.getReg()) { + /* + * If the register remains unchanged, we're only changing + * the type or local var name so don't update use list + */ + getBlock().getParent().onSourceChanged(this, origSpec, newSpec); + } + + insn = insn.withNewRegisters(getResult(), newSources); + } + + /** + * Changes the source list of the insn. New source list should be the + * same size and consist of sources of identical types. + * + * @param newSources non-null new sources list. + */ + public final void setNewSources(RegisterSpecList newSources) { + RegisterSpecList origSources = insn.getSources(); + + if (origSources.size() != newSources.size()) { + throw new RuntimeException("Sources counts don't match"); + } + + insn = insn.withNewRegisters(getResult(), newSources); + } + + /** {@inheritDoc} */ + @Override + public NormalSsaInsn clone() { + return (NormalSsaInsn) super.clone(); + } + + /** + * Like rop.Insn.getSources(). + * + * @return {@code null-ok;} sources list + */ + @Override + public RegisterSpecList getSources() { + return insn.getSources(); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return toRopInsn().toHuman(); + } + + /** {@inheritDoc} */ + @Override + public Insn toRopInsn() { + return insn.withNewRegisters(getResult(), insn.getSources()); + } + + /** + * @return the Rop opcode for this insn + */ + @Override + public Rop getOpcode() { + return insn.getOpcode(); + } + + /** {@inheritDoc} */ + @Override + public Insn getOriginalRopInsn() { + return insn; + } + + /** {@inheritDoc} */ + @Override + public RegisterSpec getLocalAssignment() { + RegisterSpec assignment; + + if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) { + assignment = insn.getSources().get(0); + } else { + assignment = getResult(); + } + + if (assignment == null) { + return null; + } + + LocalItem local = assignment.getLocalItem(); + + if (local == null) { + return null; + } + + return assignment; + } + + /** + * Upgrades this insn to a version that represents the constant source + * literally. If the upgrade is not possible, this does nothing. + * + * @see Insn#withSourceLiteral + */ + public void upgradeToLiteral() { + RegisterSpecList oldSources = insn.getSources(); + + insn = insn.withSourceLiteral(); + getBlock().getParent().onSourcesChanged(this, oldSources); + } + + /** + * @return true if this is a move (but not a move-operand) instruction + */ + @Override + public boolean isNormalMoveInsn() { + return insn.getOpcode().getOpcode() == RegOps.MOVE; + } + + /** {@inheritDoc} */ + @Override + public boolean isMoveException() { + return insn.getOpcode().getOpcode() == RegOps.MOVE_EXCEPTION; + } + + /** {@inheritDoc} */ + @Override + public boolean canThrow() { + return insn.canThrow(); + } + + /** {@inheritDoc} */ + @Override + public void accept(Visitor v) { + if (isNormalMoveInsn()) { + v.visitMoveInsn(this); + } else { + v.visitNonMoveInsn(this); + } + } + + /** {@inheritDoc} */ + @Override + public boolean isPhiOrMove() { + return isNormalMoveInsn(); + } + + /** + * {@inheritDoc} + * + * TODO(dx team): Increase the scope of this. + */ + @Override + public boolean hasSideEffect() { + Rop opcode = getOpcode(); + + if (opcode.getBranchingness() != Rop.BRANCH_NONE) { + return true; + } + + boolean hasLocalSideEffect = Optimizer.getPreserveLocals() && getLocalAssignment() != null; + + switch (opcode.getOpcode()) { + case RegOps.MOVE_RESULT: + case RegOps.MOVE: + case RegOps.CONST: + return hasLocalSideEffect; + default: + return true; + } + } } diff --git a/dx/src/com/android/jack/dx/ssa/Optimizer.java b/dx/src/com/android/jack/dx/ssa/Optimizer.java index 799b704..35d6460 100644 --- a/dx/src/com/android/jack/dx/ssa/Optimizer.java +++ b/dx/src/com/android/jack/dx/ssa/Optimizer.java @@ -28,230 +28,229 @@ import java.util.EnumSet; * and returns it to rop form. */ public class Optimizer { - private static boolean preserveLocals = true; + private static boolean preserveLocals = true; + + private static TranslationAdvice advice; + + /** optional optimizer steps */ + public enum OptionalStep { + MOVE_PARAM_COMBINER, SCCP, LITERAL_UPGRADE, CONST_COLLECTOR, ESCAPE_ANALYSIS + } + + /** + * @return true if local variable information should be preserved, even + * at code size/register size cost + */ + public static boolean getPreserveLocals() { + return preserveLocals; + } + + /** + * @return {@code non-null;} translation advice + */ + public static TranslationAdvice getAdvice() { + return advice; + } + + /** + * Runs optimization algorthims over this method, and returns a new + * instance of RopMethod with the changes. + * + * @param rmeth method to process + * @param paramWidth the total width, in register-units, of this method's + * parameters + * @param isStatic true if this method has no 'this' pointer argument. + * @param inPreserveLocals true if local variable info should be preserved, + * at the cost of some registers and insns + * @param inAdvice {@code non-null;} translation advice + * @return optimized method + */ + public static RopMethod optimize(RopMethod rmeth, int paramWidth, boolean isStatic, + boolean inPreserveLocals, TranslationAdvice inAdvice) { + + return optimize(rmeth, + paramWidth, + isStatic, + inPreserveLocals, + inAdvice, + EnumSet.allOf(OptionalStep.class)); + } + + /** + * Runs optimization algorthims over this method, and returns a new + * instance of RopMethod with the changes. + * + * @param rmeth method to process + * @param paramWidth the total width, in register-units, of this method's + * parameters + * @param isStatic true if this method has no 'this' pointer argument. + * @param inPreserveLocals true if local variable info should be preserved, + * at the cost of some registers and insns + * @param inAdvice {@code non-null;} translation advice + * @param steps set of optional optimization steps to run + * @return optimized method + */ + public static RopMethod optimize(RopMethod rmeth, + int paramWidth, + boolean isStatic, + boolean inPreserveLocals, + TranslationAdvice inAdvice, + EnumSet<OptionalStep> steps) { + SsaMethod ssaMeth = null; + + preserveLocals = inPreserveLocals; + advice = inAdvice; + + ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); + runSsaFormSteps(ssaMeth, steps); + + RopMethod resultMeth = SsaToRop.convertToRopMethod(ssaMeth, false); + + if (resultMeth.getBlocks().getRegCount() > advice.getMaxOptimalRegisterCount()) { + // Try to see if we can squeeze it under the register count bar + resultMeth = optimizeMinimizeRegisters(rmeth, paramWidth, isStatic, steps); + } + return resultMeth; + } + + /** + * Runs the optimizer with a strategy to minimize the number of rop-form + * registers used by the end result. Dex bytecode does not have instruction + * forms that take register numbers larger than 15 for all instructions. + * If we've produced a method that uses more than 16 registers, try again + * with a different strategy to see if we can get under the bar. The end + * result will be much more efficient. + * + * @param rmeth method to process + * @param paramWidth the total width, in register-units, of this method's + * parameters + * @param isStatic true if this method has no 'this' pointer argument. + * @param steps set of optional optimization steps to run + * @return optimized method + */ + private static RopMethod optimizeMinimizeRegisters(RopMethod rmeth, int paramWidth, + boolean isStatic, EnumSet<OptionalStep> steps) { + SsaMethod ssaMeth; + RopMethod resultMeth; + + ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); + + EnumSet<OptionalStep> newSteps = steps.clone(); + + /* + * CONST_COLLECTOR trades insns for registers, which is not an + * appropriate strategy here. + */ + newSteps.remove(OptionalStep.CONST_COLLECTOR); - private static TranslationAdvice advice; + runSsaFormSteps(ssaMeth, newSteps); - /** optional optimizer steps */ - public enum OptionalStep { - MOVE_PARAM_COMBINER, SCCP, LITERAL_UPGRADE, CONST_COLLECTOR, - ESCAPE_ANALYSIS - } + resultMeth = SsaToRop.convertToRopMethod(ssaMeth, true); + return resultMeth; + } - /** - * @return true if local variable information should be preserved, even - * at code size/register size cost - */ - public static boolean getPreserveLocals() { - return preserveLocals; - } + private static void runSsaFormSteps(SsaMethod ssaMeth, EnumSet<OptionalStep> steps) { + boolean needsDeadCodeRemover = true; - /** - * @return {@code non-null;} translation advice - */ - public static TranslationAdvice getAdvice() { - return advice; + if (steps.contains(OptionalStep.MOVE_PARAM_COMBINER)) { + MoveParamCombiner.process(ssaMeth); } - /** - * Runs optimization algorthims over this method, and returns a new - * instance of RopMethod with the changes. - * - * @param rmeth method to process - * @param paramWidth the total width, in register-units, of this method's - * parameters - * @param isStatic true if this method has no 'this' pointer argument. - * @param inPreserveLocals true if local variable info should be preserved, - * at the cost of some registers and insns - * @param inAdvice {@code non-null;} translation advice - * @return optimized method - */ - public static RopMethod optimize(RopMethod rmeth, int paramWidth, - boolean isStatic, boolean inPreserveLocals, - TranslationAdvice inAdvice) { - - return optimize(rmeth, paramWidth, isStatic, inPreserveLocals, inAdvice, - EnumSet.allOf(OptionalStep.class)); + if (steps.contains(OptionalStep.SCCP)) { + SCCP.process(ssaMeth); + DeadCodeRemover.process(ssaMeth); + needsDeadCodeRemover = false; } - /** - * Runs optimization algorthims over this method, and returns a new - * instance of RopMethod with the changes. - * - * @param rmeth method to process - * @param paramWidth the total width, in register-units, of this method's - * parameters - * @param isStatic true if this method has no 'this' pointer argument. - * @param inPreserveLocals true if local variable info should be preserved, - * at the cost of some registers and insns - * @param inAdvice {@code non-null;} translation advice - * @param steps set of optional optimization steps to run - * @return optimized method - */ - public static RopMethod optimize(RopMethod rmeth, int paramWidth, - boolean isStatic, boolean inPreserveLocals, - TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) { - SsaMethod ssaMeth = null; - - preserveLocals = inPreserveLocals; - advice = inAdvice; - - ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); - runSsaFormSteps(ssaMeth, steps); - - RopMethod resultMeth = SsaToRop.convertToRopMethod(ssaMeth, false); - - if (resultMeth.getBlocks().getRegCount() - > advice.getMaxOptimalRegisterCount()) { - // Try to see if we can squeeze it under the register count bar - resultMeth = optimizeMinimizeRegisters(rmeth, paramWidth, isStatic, - steps); - } - return resultMeth; + if (steps.contains(OptionalStep.LITERAL_UPGRADE)) { + LiteralOpUpgrader.process(ssaMeth); + DeadCodeRemover.process(ssaMeth); + needsDeadCodeRemover = false; } - /** - * Runs the optimizer with a strategy to minimize the number of rop-form - * registers used by the end result. Dex bytecode does not have instruction - * forms that take register numbers larger than 15 for all instructions. - * If we've produced a method that uses more than 16 registers, try again - * with a different strategy to see if we can get under the bar. The end - * result will be much more efficient. - * - * @param rmeth method to process - * @param paramWidth the total width, in register-units, of this method's - * parameters - * @param isStatic true if this method has no 'this' pointer argument. - * @param steps set of optional optimization steps to run - * @return optimized method + /* + * ESCAPE_ANALYSIS impacts debuggability, so left off by default */ - private static RopMethod optimizeMinimizeRegisters(RopMethod rmeth, - int paramWidth, boolean isStatic, - EnumSet<OptionalStep> steps) { - SsaMethod ssaMeth; - RopMethod resultMeth; - - ssaMeth = SsaConverter.convertToSsaMethod( - rmeth, paramWidth, isStatic); - - EnumSet<OptionalStep> newSteps = steps.clone(); - - /* - * CONST_COLLECTOR trades insns for registers, which is not an - * appropriate strategy here. - */ - newSteps.remove(OptionalStep.CONST_COLLECTOR); - - runSsaFormSteps(ssaMeth, newSteps); + steps.remove(OptionalStep.ESCAPE_ANALYSIS); + if (steps.contains(OptionalStep.ESCAPE_ANALYSIS)) { + EscapeAnalysis.process(ssaMeth); + DeadCodeRemover.process(ssaMeth); + needsDeadCodeRemover = false; + } - resultMeth = SsaToRop.convertToRopMethod(ssaMeth, true); - return resultMeth; + if (steps.contains(OptionalStep.CONST_COLLECTOR)) { + ConstCollector.process(ssaMeth); + DeadCodeRemover.process(ssaMeth); + needsDeadCodeRemover = false; } - private static void runSsaFormSteps(SsaMethod ssaMeth, - EnumSet<OptionalStep> steps) { - boolean needsDeadCodeRemover = true; - - if (steps.contains(OptionalStep.MOVE_PARAM_COMBINER)) { - MoveParamCombiner.process(ssaMeth); - } - - if (steps.contains(OptionalStep.SCCP)) { - SCCP.process(ssaMeth); - DeadCodeRemover.process(ssaMeth); - needsDeadCodeRemover = false; - } - - if (steps.contains(OptionalStep.LITERAL_UPGRADE)) { - LiteralOpUpgrader.process(ssaMeth); - DeadCodeRemover.process(ssaMeth); - needsDeadCodeRemover = false; - } - - /* - * ESCAPE_ANALYSIS impacts debuggability, so left off by default - */ - steps.remove(OptionalStep.ESCAPE_ANALYSIS); - if (steps.contains(OptionalStep.ESCAPE_ANALYSIS)) { - EscapeAnalysis.process(ssaMeth); - DeadCodeRemover.process(ssaMeth); - needsDeadCodeRemover = false; - } - - if (steps.contains(OptionalStep.CONST_COLLECTOR)) { - ConstCollector.process(ssaMeth); - DeadCodeRemover.process(ssaMeth); - needsDeadCodeRemover = false; - } - - // dead code remover must be run before phi type resolver - if (needsDeadCodeRemover) { - DeadCodeRemover.process(ssaMeth); - } - - PhiTypeResolver.process(ssaMeth); + // dead code remover must be run before phi type resolver + if (needsDeadCodeRemover) { + DeadCodeRemover.process(ssaMeth); } - public static SsaMethod debugEdgeSplit(RopMethod rmeth, int paramWidth, - boolean isStatic, boolean inPreserveLocals, - TranslationAdvice inAdvice) { + PhiTypeResolver.process(ssaMeth); + } - preserveLocals = inPreserveLocals; - advice = inAdvice; + public static SsaMethod debugEdgeSplit(RopMethod rmeth, int paramWidth, boolean isStatic, + boolean inPreserveLocals, TranslationAdvice inAdvice) { - return SsaConverter.testEdgeSplit(rmeth, paramWidth, isStatic); - } + preserveLocals = inPreserveLocals; + advice = inAdvice; - public static SsaMethod debugPhiPlacement(RopMethod rmeth, int paramWidth, - boolean isStatic, boolean inPreserveLocals, - TranslationAdvice inAdvice) { + return SsaConverter.testEdgeSplit(rmeth, paramWidth, isStatic); + } - preserveLocals = inPreserveLocals; - advice = inAdvice; + public static SsaMethod debugPhiPlacement(RopMethod rmeth, int paramWidth, boolean isStatic, + boolean inPreserveLocals, TranslationAdvice inAdvice) { - return SsaConverter.testPhiPlacement(rmeth, paramWidth, isStatic); - } + preserveLocals = inPreserveLocals; + advice = inAdvice; - public static SsaMethod debugRenaming(RopMethod rmeth, int paramWidth, - boolean isStatic, boolean inPreserveLocals, - TranslationAdvice inAdvice) { + return SsaConverter.testPhiPlacement(rmeth, paramWidth, isStatic); + } - preserveLocals = inPreserveLocals; - advice = inAdvice; + public static SsaMethod debugRenaming(RopMethod rmeth, int paramWidth, boolean isStatic, + boolean inPreserveLocals, TranslationAdvice inAdvice) { - return SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); - } + preserveLocals = inPreserveLocals; + advice = inAdvice; - public static SsaMethod debugDeadCodeRemover(RopMethod rmeth, - int paramWidth, boolean isStatic, boolean inPreserveLocals, - TranslationAdvice inAdvice) { + return SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); + } - SsaMethod ssaMeth; + public static SsaMethod debugDeadCodeRemover(RopMethod rmeth, int paramWidth, boolean isStatic, + boolean inPreserveLocals, TranslationAdvice inAdvice) { - preserveLocals = inPreserveLocals; - advice = inAdvice; + SsaMethod ssaMeth; - ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); - DeadCodeRemover.process(ssaMeth); + preserveLocals = inPreserveLocals; + advice = inAdvice; - return ssaMeth; - } + ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); + DeadCodeRemover.process(ssaMeth); - public static SsaMethod debugNoRegisterAllocation(RopMethod rmeth, - int paramWidth, boolean isStatic, boolean inPreserveLocals, - TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) { + return ssaMeth; + } - SsaMethod ssaMeth; + public static SsaMethod debugNoRegisterAllocation(RopMethod rmeth, + int paramWidth, + boolean isStatic, + boolean inPreserveLocals, + TranslationAdvice inAdvice, + EnumSet<OptionalStep> steps) { - preserveLocals = inPreserveLocals; - advice = inAdvice; + SsaMethod ssaMeth; - ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); + preserveLocals = inPreserveLocals; + advice = inAdvice; - runSsaFormSteps(ssaMeth, steps); + ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic); - LivenessAnalyzer.constructInterferenceGraph(ssaMeth); + runSsaFormSteps(ssaMeth, steps); - return ssaMeth; - } + LivenessAnalyzer.constructInterferenceGraph(ssaMeth); + + return ssaMeth; + } } diff --git a/dx/src/com/android/jack/dx/ssa/PhiInsn.java b/dx/src/com/android/jack/dx/ssa/PhiInsn.java index 9372305..d7419d6 100644 --- a/dx/src/com/android/jack/dx/ssa/PhiInsn.java +++ b/dx/src/com/android/jack/dx/ssa/PhiInsn.java @@ -16,7 +16,12 @@ package com.android.jack.dx.ssa; -import com.android.jack.dx.rop.code.*; +import com.android.jack.dx.rop.code.Insn; +import com.android.jack.dx.rop.code.LocalItem; +import com.android.jack.dx.rop.code.RegisterSpec; +import com.android.jack.dx.rop.code.RegisterSpecList; +import com.android.jack.dx.rop.code.Rop; +import com.android.jack.dx.rop.code.SourcePosition; import com.android.jack.dx.rop.type.Type; import com.android.jack.dx.rop.type.TypeBearer; import com.android.jack.dx.util.Hex; @@ -30,369 +35,362 @@ import java.util.List; * conversion back to ROP form. */ public final class PhiInsn extends SsaInsn { - /** - * result register. The original result register of the phi insn - * is needed during the renaming process after the new result - * register has already been chosen. + /** + * result register. The original result register of the phi insn + * is needed during the renaming process after the new result + * register has already been chosen. + */ + private final int ropResultReg; + + /** + * {@code non-null;} operands of the instruction; built up by + * {@link #addPhiOperand} + */ + private final ArrayList<Operand> operands = new ArrayList<Operand>(); + + /** {@code null-ok;} source registers; constructed lazily */ + private RegisterSpecList sources; + + /** + * Constructs a new phi insn with no operands. + * + * @param resultReg the result reg for this phi insn + * @param block block containing this insn. + */ + public PhiInsn(RegisterSpec resultReg, SsaBasicBlock block) { + super(resultReg, block); + ropResultReg = resultReg.getReg(); + } + + /** + * Makes a phi insn with a void result type. + * + * @param resultReg the result register for this phi insn. + * @param block block containing this insn. + */ + public PhiInsn(final int resultReg, final SsaBasicBlock block) { + /* + * The result type here is bogus: The type depends on the + * operand and will be derived later. */ - private final int ropResultReg; - - /** - * {@code non-null;} operands of the instruction; built up by - * {@link #addPhiOperand} - */ - private final ArrayList<Operand> operands = new ArrayList<Operand>(); - - /** {@code null-ok;} source registers; constructed lazily */ - private RegisterSpecList sources; - - /** - * Constructs a new phi insn with no operands. - * - * @param resultReg the result reg for this phi insn - * @param block block containing this insn. - */ - public PhiInsn(RegisterSpec resultReg, SsaBasicBlock block) { - super(resultReg, block); - ropResultReg = resultReg.getReg(); + super(RegisterSpec.make(resultReg, Type.VOID), block); + ropResultReg = resultReg; + } + + /** {@inheritDoc} */ + @Override + public PhiInsn clone() { + throw new UnsupportedOperationException("can't clone phi"); + } + + /** + * Updates the TypeBearers of all the sources (phi operands) to be + * the current TypeBearer of the register-defining instruction's result. + * This is used during phi-type resolution.<p> + * + * Note that local association of operands are preserved in this step. + * + * @param ssaMeth method that contains this insn + */ + public void updateSourcesToDefinitions(SsaMethod ssaMeth) { + for (Operand o : operands) { + RegisterSpec def = ssaMeth.getDefinitionForRegister(o.regSpec.getReg()).getResult(); + + o.regSpec = o.regSpec.withType(def.getType()); } - /** - * Makes a phi insn with a void result type. - * - * @param resultReg the result register for this phi insn. - * @param block block containing this insn. - */ - public PhiInsn(final int resultReg, final SsaBasicBlock block) { - /* - * The result type here is bogus: The type depends on the - * operand and will be derived later. - */ - super(RegisterSpec.make(resultReg, Type.VOID), block); - ropResultReg = resultReg; + sources = null; + } + + /** + * Changes the result type. Used during phi type resolution + * + * @param type {@code non-null;} new TypeBearer + * @param local {@code null-ok;} new local info, if available + */ + public void changeResultType(TypeBearer type, LocalItem local) { + setResult(RegisterSpec.makeLocalOptional(getResult().getReg(), type, local)); + } + + /** + * Gets the original rop-form result reg. This is useful during renaming. + * + * @return the original rop-form result reg + */ + public int getRopResultReg() { + return ropResultReg; + } + + /** + * Adds an operand to this phi instruction. + * + * @param registerSpec register spec, including type and reg of operand + * @param predBlock predecessor block to be associated with this operand + */ + public void addPhiOperand(RegisterSpec registerSpec, SsaBasicBlock predBlock) { + operands.add(new Operand(registerSpec, predBlock.getIndex(), predBlock.getRopLabel())); + + // Un-cache sources, in case someone has already called getSources(). + sources = null; + } + + /** + * Removes all operand uses of a register from this phi instruction. + * + * @param registerSpec register spec, including type and reg of operand + */ + public void removePhiRegister(RegisterSpec registerSpec) { + ArrayList<Operand> operandsToRemove = new ArrayList<Operand>(); + for (Operand o : operands) { + if (o.regSpec.getReg() == registerSpec.getReg()) { + operandsToRemove.add(o); + } } - /** {@inheritDoc} */ - @Override - public PhiInsn clone() { - throw new UnsupportedOperationException("can't clone phi"); + operands.removeAll(operandsToRemove); + + // Un-cache sources, in case someone has already called getSources(). + sources = null; + } + + /** + * Gets the index of the pred block associated with the RegisterSpec + * at the particular getSources() index. + * + * @param sourcesIndex index of source in getSources() + * @return block index + */ + public int predBlockIndexForSourcesIndex(int sourcesIndex) { + return operands.get(sourcesIndex).blockIndex; + } + + /** + * {@inheritDoc} + * + * Always returns null for {@code PhiInsn}s. + */ + @Override + public Rop getOpcode() { + return null; + } + + /** + * {@inheritDoc} + * + * Always returns null for {@code PhiInsn}s. + */ + @Override + public Insn getOriginalRopInsn() { + return null; + } + + /** + * {@inheritDoc} + * + * Always returns false for {@code PhiInsn}s. + */ + @Override + public boolean canThrow() { + return false; + } + + /** + * Gets sources. Constructed lazily from phi operand data structures and + * then cached. + * + * @return {@code non-null;} sources list + */ + @Override + public RegisterSpecList getSources() { + if (sources != null) { + return sources; } - /** - * Updates the TypeBearers of all the sources (phi operands) to be - * the current TypeBearer of the register-defining instruction's result. - * This is used during phi-type resolution.<p> - * - * Note that local association of operands are preserved in this step. - * - * @param ssaMeth method that contains this insn - */ - public void updateSourcesToDefinitions(SsaMethod ssaMeth) { - for (Operand o : operands) { - RegisterSpec def - = ssaMeth.getDefinitionForRegister( - o.regSpec.getReg()).getResult(); - - o.regSpec = o.regSpec.withType(def.getType()); - } - - sources = null; + if (operands.size() == 0) { + // How'd this happen? A phi insn with no operand? + return RegisterSpecList.EMPTY; } - /** - * Changes the result type. Used during phi type resolution - * - * @param type {@code non-null;} new TypeBearer - * @param local {@code null-ok;} new local info, if available - */ - public void changeResultType(TypeBearer type, LocalItem local) { - setResult(RegisterSpec.makeLocalOptional( - getResult().getReg(), type, local)); - } + int szSources = operands.size(); + sources = new RegisterSpecList(szSources); - /** - * Gets the original rop-form result reg. This is useful during renaming. - * - * @return the original rop-form result reg - */ - public int getRopResultReg() { - return ropResultReg; - } + for (int i = 0; i < szSources; i++) { + Operand o = operands.get(i); - /** - * Adds an operand to this phi instruction. - * - * @param registerSpec register spec, including type and reg of operand - * @param predBlock predecessor block to be associated with this operand - */ - public void addPhiOperand(RegisterSpec registerSpec, - SsaBasicBlock predBlock) { - operands.add(new Operand(registerSpec, predBlock.getIndex(), - predBlock.getRopLabel())); - - // Un-cache sources, in case someone has already called getSources(). - sources = null; + sources.set(i, o.regSpec); } - /** - * Removes all operand uses of a register from this phi instruction. - * - * @param registerSpec register spec, including type and reg of operand - */ - public void removePhiRegister(RegisterSpec registerSpec) { - ArrayList<Operand> operandsToRemove = new ArrayList<Operand>(); - for (Operand o : operands) { - if (o.regSpec.getReg() == registerSpec.getReg()) { - operandsToRemove.add(o); - } - } - - operands.removeAll(operandsToRemove); - - // Un-cache sources, in case someone has already called getSources(). - sources = null; - } + sources.setImmutable(); + return sources; + } - /** - * Gets the index of the pred block associated with the RegisterSpec - * at the particular getSources() index. - * - * @param sourcesIndex index of source in getSources() - * @return block index + /** {@inheritDoc} */ + @Override + public boolean isRegASource(int reg) { + /* + * Avoid creating a sources list in case it has not already been + * created. */ - public int predBlockIndexForSourcesIndex(int sourcesIndex) { - return operands.get(sourcesIndex).blockIndex; - } - - /** - * {@inheritDoc} - * - * Always returns null for {@code PhiInsn}s. - */ - @Override - public Rop getOpcode() { - return null; - } - /** - * {@inheritDoc} - * - * Always returns null for {@code PhiInsn}s. - */ - @Override - public Insn getOriginalRopInsn() { - return null; - } - - /** - * {@inheritDoc} - * - * Always returns false for {@code PhiInsn}s. - */ - @Override - public boolean canThrow() { - return false; +for (Operand o : operands) { + if (o.regSpec.getReg() == reg) { + return true; + } } - /** - * Gets sources. Constructed lazily from phi operand data structures and - * then cached. - * - * @return {@code non-null;} sources list - */ - @Override - public RegisterSpecList getSources() { - if (sources != null) { - return sources; - } - - if (operands.size() == 0) { - // How'd this happen? A phi insn with no operand? - return RegisterSpecList.EMPTY; - } - - int szSources = operands.size(); - sources = new RegisterSpecList(szSources); - - for (int i = 0; i < szSources; i++) { - Operand o = operands.get(i); + return false; + } - sources.set(i, o.regSpec); - } - - sources.setImmutable(); - return sources; + /** + * @return true if all operands use the same register + */ + public boolean areAllOperandsEqual() { + if (operands.size() == 0) { + // This should never happen. + return true; } - /** {@inheritDoc} */ - @Override - public boolean isRegASource(int reg) { - /* - * Avoid creating a sources list in case it has not already been - * created. - */ - - for (Operand o : operands) { - if (o.regSpec.getReg() == reg) { - return true; - } - } - + int firstReg = operands.get(0).regSpec.getReg(); + for (Operand o : operands) { + if (firstReg != o.regSpec.getReg()) { return false; + } } - /** - * @return true if all operands use the same register - */ - public boolean areAllOperandsEqual() { - if (operands.size() == 0 ) { - // This should never happen. - return true; - } - - int firstReg = operands.get(0).regSpec.getReg(); - for (Operand o : operands) { - if (firstReg != o.regSpec.getReg()) { - return false; - } - } - - return true; + return true; + } + + /** {@inheritDoc} */ + @Override + public final void mapSourceRegisters(RegisterMapper mapper) { + for (Operand o : operands) { + RegisterSpec old = o.regSpec; + o.regSpec = mapper.map(old); + if (old != o.regSpec) { + getBlock().getParent().onSourceChanged(this, old, o.regSpec); + } } - - /** {@inheritDoc} */ - @Override - public final void mapSourceRegisters(RegisterMapper mapper) { - for (Operand o : operands) { - RegisterSpec old = o.regSpec; - o.regSpec = mapper.map(old); - if (old != o.regSpec) { - getBlock().getParent().onSourceChanged(this, old, o.regSpec); - } - } - sources = null; + sources = null; + } + + /** + * Always throws an exeption, since a phi insn may not be + * converted back to rop form. + * + * @return always throws exception + */ + @Override + public Insn toRopInsn() { + throw new IllegalArgumentException("Cannot convert phi insns to rop form"); + } + + /** + * Returns the list of predecessor blocks associated with all operands + * that have {@code reg} as an operand register. + * + * @param reg register to look up + * @param ssaMeth method we're operating on + * @return list of predecessor blocks, empty if none + */ + public List<SsaBasicBlock> predBlocksForReg(int reg, SsaMethod ssaMeth) { + ArrayList<SsaBasicBlock> ret = new ArrayList<SsaBasicBlock>(); + + for (Operand o : operands) { + if (o.regSpec.getReg() == reg) { + ret.add(ssaMeth.getBlocks().get(o.blockIndex)); + } } - /** - * Always throws an exeption, since a phi insn may not be - * converted back to rop form. - * - * @return always throws exception - */ - @Override - public Insn toRopInsn() { - throw new IllegalArgumentException( - "Cannot convert phi insns to rop form"); + return ret; + } + + /** {@inheritDoc} */ + @Override + public boolean isPhiOrMove() { + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean hasSideEffect() { + return Optimizer.getPreserveLocals() && getLocalAssignment() != null; + } + + /** {@inheritDoc} */ + @Override + public void accept(SsaInsn.Visitor v) { + v.visitPhiInsn(this); + } + + /** {@inheritDoc} */ + @Override + public String toHuman() { + return toHumanWithInline(null); + } + + /** + * Returns human-readable string for listing dumps. This method + * allows sub-classes to specify extra text. + * + * @param extra {@code null-ok;} the argument to print after the opcode + * @return human-readable string for listing dumps + */ + protected final String toHumanWithInline(String extra) { + StringBuffer sb = new StringBuffer(80); + + sb.append(SourcePosition.NO_INFO); + sb.append(": phi"); + + if (extra != null) { + sb.append("("); + sb.append(extra); + sb.append(")"); } - /** - * Returns the list of predecessor blocks associated with all operands - * that have {@code reg} as an operand register. - * - * @param reg register to look up - * @param ssaMeth method we're operating on - * @return list of predecessor blocks, empty if none - */ - public List<SsaBasicBlock> predBlocksForReg(int reg, SsaMethod ssaMeth) { - ArrayList<SsaBasicBlock> ret = new ArrayList<SsaBasicBlock>(); - - for (Operand o : operands) { - if (o.regSpec.getReg() == reg) { - ret.add(ssaMeth.getBlocks().get(o.blockIndex)); - } - } + RegisterSpec result = getResult(); - return ret; + if (result == null) { + sb.append(" ."); + } else { + sb.append(" "); + sb.append(result.toHuman()); } - /** {@inheritDoc} */ - @Override - public boolean isPhiOrMove() { - return true; - } - - /** {@inheritDoc} */ - @Override - public boolean hasSideEffect() { - return Optimizer.getPreserveLocals() && getLocalAssignment() != null; - } + sb.append(" <-"); - /** {@inheritDoc} */ - @Override - public void accept(SsaInsn.Visitor v) { - v.visitPhiInsn(this); + int sz = getSources().size(); + if (sz == 0) { + sb.append(" ."); + } else { + for (int i = 0; i < sz; i++) { + sb.append(" "); + sb.append(sources.get(i).toHuman() + "[b=" + Hex.u2(operands.get(i).ropLabel) + "]"); + } } - /** {@inheritDoc} */ - public String toHuman() { - return toHumanWithInline(null); - } - - /** - * Returns human-readable string for listing dumps. This method - * allows sub-classes to specify extra text. - * - * @param extra {@code null-ok;} the argument to print after the opcode - * @return human-readable string for listing dumps - */ - protected final String toHumanWithInline(String extra) { - StringBuffer sb = new StringBuffer(80); - - sb.append(SourcePosition.NO_INFO); - sb.append(": phi"); - - if (extra != null) { - sb.append("("); - sb.append(extra); - sb.append(")"); - } - - RegisterSpec result = getResult(); - - if (result == null) { - sb.append(" ."); - } else { - sb.append(" "); - sb.append(result.toHuman()); - } - - sb.append(" <-"); - - int sz = getSources().size(); - if (sz == 0) { - sb.append(" ."); - } else { - for (int i = 0; i < sz; i++) { - sb.append(" "); - sb.append(sources.get(i).toHuman() - + "[b=" - + Hex.u2(operands.get(i).ropLabel) + "]"); - } - } - - return sb.toString(); - } - - /** - * A single phi operand, consiting of source register and block index - * for move. - */ - private static class Operand { - public RegisterSpec regSpec; - public final int blockIndex; - public final int ropLabel; // only used for debugging - - public Operand(RegisterSpec regSpec, int blockIndex, int ropLabel) { - this.regSpec = regSpec; - this.blockIndex = blockIndex; - this.ropLabel = ropLabel; - } - } - - /** - * Visitor interface for instances of this (outer) class. - */ - public static interface Visitor { - public void visitPhiInsn(PhiInsn insn); + return sb.toString(); + } + + /** + * A single phi operand, consiting of source register and block index + * for move. + */ + private static class Operand { + public RegisterSpec regSpec; + public final int blockIndex; + public final int ropLabel; // only used for debugging + + public Operand(RegisterSpec regSpec, int blockIndex, int ropLabel) { + this.regSpec = regSpec; + this.blockIndex = blockIndex; + this.ropLabel = ropLabel; } + } + + /** + * Visitor interface for instances of this (outer) class. + */ + public static interface Visitor { + public void visitPhiInsn(PhiInsn insn); + } } diff --git a/dx/src/com/android/jack/dx/ssa/PhiTypeResolver.java b/dx/src/com/android/jack/dx/ssa/PhiTypeResolver.java index abc1253..896f816 100644 --- a/dx/src/com/android/jack/dx/ssa/PhiTypeResolver.java +++ b/dx/src/com/android/jack/dx/ssa/PhiTypeResolver.java @@ -42,222 +42,218 @@ import java.util.List; */ public class PhiTypeResolver { - SsaMethod ssaMeth; - /** indexed by register; all registers still defined by unresolved phis */ - private final BitSet worklist; - - /** - * Resolves all phi types in the method - * @param ssaMeth method to process - */ - public static void process (SsaMethod ssaMeth) { - new PhiTypeResolver(ssaMeth).run(); + SsaMethod ssaMeth; + /** indexed by register; all registers still defined by unresolved phis */ + private final BitSet worklist; + + /** + * Resolves all phi types in the method + * @param ssaMeth method to process + */ + public static void process(SsaMethod ssaMeth) { + new PhiTypeResolver(ssaMeth).run(); + } + + private PhiTypeResolver(SsaMethod ssaMeth) { + this.ssaMeth = ssaMeth; + worklist = new BitSet(ssaMeth.getRegCount()); + } + + /** + * Runs the phi-type resolver. + */ + private void run() { + + int regCount = ssaMeth.getRegCount(); + + for (int reg = 0; reg < regCount; reg++) { + SsaInsn definsn = ssaMeth.getDefinitionForRegister(reg); + + if (definsn != null && (definsn.getResult().getBasicType() == Type.BT_VOID)) { + worklist.set(reg); + } } - private PhiTypeResolver(SsaMethod ssaMeth) { - this.ssaMeth = ssaMeth; - worklist = new BitSet(ssaMeth.getRegCount()); - } - - /** - * Runs the phi-type resolver. - */ - private void run() { - - int regCount = ssaMeth.getRegCount(); - - for (int reg = 0; reg < regCount; reg++) { - SsaInsn definsn = ssaMeth.getDefinitionForRegister(reg); - - if (definsn != null - && (definsn.getResult().getBasicType() == Type.BT_VOID)) { - worklist.set(reg); - } - } - - int reg; - while ( 0 <= (reg = worklist.nextSetBit(0))) { - worklist.clear(reg); - - /* - * definitions on the worklist have a type of BT_VOID, which - * must have originated from a PhiInsn. - */ - PhiInsn definsn = (PhiInsn)ssaMeth.getDefinitionForRegister(reg); - - if (resolveResultType(definsn)) { - /* - * If the result type has changed, re-resolve all phis - * that use this. - */ - - List<SsaInsn> useList = ssaMeth.getUseListForRegister(reg); - - int sz = useList.size(); - for (int i = 0; i < sz; i++ ) { - SsaInsn useInsn = useList.get(i); - RegisterSpec resultReg = useInsn.getResult(); - if (resultReg != null && useInsn instanceof PhiInsn) { - worklist.set(resultReg.getReg()); - } - } - } + int reg; + while (0 <= (reg = worklist.nextSetBit(0))) { + worklist.clear(reg); + + /* + * definitions on the worklist have a type of BT_VOID, which + * must have originated from a PhiInsn. + */ + PhiInsn definsn = (PhiInsn) ssaMeth.getDefinitionForRegister(reg); + + if (resolveResultType(definsn)) { + /* + * If the result type has changed, re-resolve all phis + * that use this. + */ + +List<SsaInsn> useList = ssaMeth.getUseListForRegister(reg); + + int sz = useList.size(); + for (int i = 0; i < sz; i++) { + SsaInsn useInsn = useList.get(i); + RegisterSpec resultReg = useInsn.getResult(); + if (resultReg != null && useInsn instanceof PhiInsn) { + worklist.set(resultReg.getReg()); + } } + } } - - /** - * Returns true if a and b are equal, whether - * or not either of them are null. - * @param a - * @param b - * @return true if equal - */ - private static boolean equalsHandlesNulls(LocalItem a, LocalItem b) { - return (a == b) || ((a != null) && a.equals(b)); + } + + /** + * Returns true if a and b are equal, whether + * or not either of them are null. + * @param a + * @param b + * @return true if equal + */ + private static boolean equalsHandlesNulls(LocalItem a, LocalItem b) { + return (a == b) || ((a != null) && a.equals(b)); + } + + /** + * Resolves the result of a phi insn based on its operands. The "void" + * type, which is a nonsensical type for a register, is used for + * registers defined by as-of-yet-unresolved phi operations. + * + * @return true if the result type changed, false if no change + */ + boolean resolveResultType(PhiInsn insn) { + insn.updateSourcesToDefinitions(ssaMeth); + + RegisterSpecList sources = insn.getSources(); + + // Start by finding the first non-void operand + RegisterSpec first = null; + int firstIndex = -1; + + int szSources = sources.size(); + for (int i = 0; i < szSources; i++) { + RegisterSpec rs = sources.get(i); + + if (rs.getBasicType() != Type.BT_VOID) { + first = rs; + firstIndex = i; + } } - /** - * Resolves the result of a phi insn based on its operands. The "void" - * type, which is a nonsensical type for a register, is used for - * registers defined by as-of-yet-unresolved phi operations. - * - * @return true if the result type changed, false if no change - */ - boolean resolveResultType(PhiInsn insn) { - insn.updateSourcesToDefinitions(ssaMeth); - - RegisterSpecList sources = insn.getSources(); - - // Start by finding the first non-void operand - RegisterSpec first = null; - int firstIndex = -1; - - int szSources = sources.size(); - for (int i = 0 ; i <szSources ; i++) { - RegisterSpec rs = sources.get(i); - - if (rs.getBasicType() != Type.BT_VOID) { - first = rs; - firstIndex = i; - } - } - - if (first == null) { - // All operands are void -- we're not ready to resolve yet - return false; - } - - LocalItem firstLocal = first.getLocalItem(); - TypeBearer mergedType = first.getType(); - boolean sameLocals = true; - for (int i = 0 ; i < szSources ; i++) { - if (i == firstIndex) { - continue; - } - - RegisterSpec rs = sources.get(i); + if (first == null) { + // All operands are void -- we're not ready to resolve yet + return false; + } - // Just skip void (unresolved phi results) for now - if (rs.getBasicType() == Type.BT_VOID){ - continue; - } + LocalItem firstLocal = first.getLocalItem(); + TypeBearer mergedType = first.getType(); + boolean sameLocals = true; + for (int i = 0; i < szSources; i++) { + if (i == firstIndex) { + continue; + } - sameLocals = sameLocals - && equalsHandlesNulls(firstLocal, rs.getLocalItem()); + RegisterSpec rs = sources.get(i); - mergedType = mergeType(mergedType, rs.getType()); - } + // Just skip void (unresolved phi results) for now + if (rs.getBasicType() == Type.BT_VOID) { + continue; + } - TypeBearer newResultType; + sameLocals = sameLocals && equalsHandlesNulls(firstLocal, rs.getLocalItem()); - if (mergedType != null) { - newResultType = mergedType; - } else { - StringBuilder sb = new StringBuilder(); + mergedType = mergeType(mergedType, rs.getType()); + } - for (int i = 0; i < szSources; i++) { - sb.append(sources.get(i).toString()); - sb.append(' '); - } + TypeBearer newResultType; - throw new RuntimeException ("Couldn't map types in phi insn:" + sb); - } + if (mergedType != null) { + newResultType = mergedType; + } else { + StringBuilder sb = new StringBuilder(); - LocalItem newLocal = sameLocals ? firstLocal : null; + for (int i = 0; i < szSources; i++) { + sb.append(sources.get(i).toString()); + sb.append(' '); + } - RegisterSpec result = insn.getResult(); + throw new RuntimeException("Couldn't map types in phi insn:" + sb); + } - if ((result.getTypeBearer() == newResultType) - && equalsHandlesNulls(newLocal, result.getLocalItem())) { - return false; - } + LocalItem newLocal = sameLocals ? firstLocal : null; - insn.changeResultType(newResultType, newLocal); + RegisterSpec result = insn.getResult(); - return true; + if ((result.getTypeBearer() == newResultType) + && equalsHandlesNulls(newLocal, result.getLocalItem())) { + return false; } - /** - * Merges two frame types. - * - * @param ft1 {@code non-null;} a frame type - * @param ft2 {@code non-null;} another frame type - * @return {@code non-null;} the result of merging the two types - */ - private static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) { - if ((ft1 == null) || ft1.equals(ft2)) { - return ft1; - } else if (ft2 == null) { - return null; + insn.changeResultType(newResultType, newLocal); + + return true; + } + + /** + * Merges two frame types. + * + * @param ft1 {@code non-null;} a frame type + * @param ft2 {@code non-null;} another frame type + * @return {@code non-null;} the result of merging the two types + */ + private static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) { + if ((ft1 == null) || ft1.equals(ft2)) { + return ft1; + } else if (ft2 == null) { + return null; + } else { + Type type1 = ft1.getType(); + Type type2 = ft2.getType(); + + if (type1 == type2) { + return type1; + } else if (type1.isReference() && type2.isReference()) { + if (type1 == Type.KNOWN_NULL) { + /* + * A known-null merges with any other reference type to + * be that reference type. + */ + return type2; + } else if (type2 == Type.KNOWN_NULL) { + /* + * The same as above, but this time it's type2 that's + * the known-null. + */ + return type1; + } else if (type1.isArray() && type2.isArray()) { + TypeBearer componentUnion = mergeType(type1.getComponentType(), type2.getComponentType()); + if (componentUnion == null) { + /* + * At least one of the types is a primitive type, + * so the merged result is just Object. + */ + return Type.OBJECT; + } + return ((Type) componentUnion).getArrayType(); } else { - Type type1 = ft1.getType(); - Type type2 = ft2.getType(); - - if (type1 == type2) { - return type1; - } else if (type1.isReference() && type2.isReference()) { - if (type1 == Type.KNOWN_NULL) { - /* - * A known-null merges with any other reference type to - * be that reference type. - */ - return type2; - } else if (type2 == Type.KNOWN_NULL) { - /* - * The same as above, but this time it's type2 that's - * the known-null. - */ - return type1; - } else if (type1.isArray() && type2.isArray()) { - TypeBearer componentUnion = - mergeType(type1.getComponentType(), - type2.getComponentType()); - if (componentUnion == null) { - /* - * At least one of the types is a primitive type, - * so the merged result is just Object. - */ - return Type.OBJECT; - } - return ((Type) componentUnion).getArrayType(); - } else { - /* - * All other unequal reference types get merged to be - * Object in this phase. This is fine here, but it - * won't be the right thing to do in the verifier. - */ - return Type.OBJECT; - } - } else if (type1.isIntlike() && type2.isIntlike()) { - /* - * Merging two non-identical int-like types results in - * the type int. - */ - return Type.INT; - } else { - return null; - } + /* + * All other unequal reference types get merged to be + * Object in this phase. This is fine here, but it + * won't be the right thing to do in the verifier. + */ + return Type.OBJECT; } + } else if (type1.isIntlike() && type2.isIntlike()) { + /* + * Merging two non-identical int-like types results in + * the type int. + */ + return Type.INT; + } else { + return null; + } } + } } diff --git a/dx/src/com/android/jack/dx/ssa/RegisterMapper.java b/dx/src/com/android/jack/dx/ssa/RegisterMapper.java index 300d956..2807fdb 100644 --- a/dx/src/com/android/jack/dx/ssa/RegisterMapper.java +++ b/dx/src/com/android/jack/dx/ssa/RegisterMapper.java @@ -18,7 +18,6 @@ package com.android.jack.dx.ssa; import com.android.jack.dx.rop.code.RegisterSpec; import com.android.jack.dx.rop.code.RegisterSpecList; -import com.android.jack.dx.util.ToHuman; /** * Represents a mapping between two register numbering schemes. @@ -27,35 +26,35 @@ import com.android.jack.dx.util.ToHuman; * instances of this class are passed. */ public abstract class RegisterMapper { - /** - * Gets the count of registers (really, the total register width, since - * category width is counted) in the new namespace. - * @return >= 0 width of new namespace. - */ - public abstract int getNewRegisterCount(); - - /** - * @param registerSpec old register - * @return register in new space - */ - public abstract RegisterSpec map(RegisterSpec registerSpec); - - /** - * - * @param sources old register list - * @return new mapped register list, or old if nothing has changed. - */ - public final RegisterSpecList map(RegisterSpecList sources) { - int sz = sources.size(); - RegisterSpecList newSources = new RegisterSpecList(sz); - - for (int i = 0; i < sz; i++) { - newSources.set(i, map(sources.get(i))); - } - - newSources.setImmutable(); - - // Return the old sources if nothing has changed. - return newSources.equals(sources) ? sources : newSources; + /** + * Gets the count of registers (really, the total register width, since + * category width is counted) in the new namespace. + * @return >= 0 width of new namespace. + */ + public abstract int getNewRegisterCount(); + + /** + * @param registerSpec old register + * @return register in new space + */ + public abstract RegisterSpec map(RegisterSpec registerSpec); + + /** + * + * @param sources old register list + * @return new mapped register list, or old if nothing has changed. + */ + public final RegisterSpecList map(RegisterSpecList sources) { + int sz = sources.size(); + RegisterSpecList newSources = new RegisterSpecList(sz); + + for (int i = 0; i < sz; i++) { + newSources.set(i, map(sources.get(i))); } + + newSources.setImmutable(); + + // Return the old sources if nothing has changed. + return newSources.equals(sources) ? sources : newSources; + } } diff --git a/dx/src/com/android/jack/dx/ssa/SCCP.java b/dx/src/com/android/jack/dx/ssa/SCCP.java index 9575353..4fc3a21 100644 --- a/dx/src/com/android/jack/dx/ssa/SCCP.java +++ b/dx/src/com/android/jack/dx/ssa/SCCP.java @@ -39,659 +39,646 @@ import java.util.BitSet; * Propagation algorithm. */ public class SCCP { - /** Lattice values */ - private static final int TOP = 0; - private static final int CONSTANT = 1; - private static final int VARYING = 2; - /** method we're processing */ - private SsaMethod ssaMeth; - /** ssaMeth.getRegCount() */ - private int regCount; - /** Lattice values for each SSA register */ - private int[] latticeValues; - /** For those registers that are constant, this is the constant value */ - private Constant[] latticeConstants; - /** Worklist of basic blocks to be processed */ - private ArrayList<SsaBasicBlock> cfgWorklist; - /** Worklist of executed basic blocks with phis to be processed */ - private ArrayList<SsaBasicBlock> cfgPhiWorklist; - /** Bitset containing bits for each block that has been found executable */ - private BitSet executableBlocks; - /** Worklist for SSA edges. This is a list of registers to process */ - private ArrayList<SsaInsn> ssaWorklist; - /** - * Worklist for SSA edges that represent varying values. It makes the - * algorithm much faster if you move all values to VARYING as fast as - * possible. - */ - private ArrayList<SsaInsn> varyingWorklist; - /** Worklist of potential branches to convert to gotos */ - private ArrayList<SsaInsn> branchWorklist; - - private SCCP(SsaMethod ssaMeth) { - this.ssaMeth = ssaMeth; - this.regCount = ssaMeth.getRegCount(); - this.latticeValues = new int[this.regCount]; - this.latticeConstants = new Constant[this.regCount]; - this.cfgWorklist = new ArrayList<SsaBasicBlock>(); - this.cfgPhiWorklist = new ArrayList<SsaBasicBlock>(); - this.executableBlocks = new BitSet(ssaMeth.getBlocks().size()); - this.ssaWorklist = new ArrayList<SsaInsn>(); - this.varyingWorklist = new ArrayList<SsaInsn>(); - this.branchWorklist = new ArrayList<SsaInsn>(); - for (int i = 0; i < this.regCount; i++) { - latticeValues[i] = TOP; - latticeConstants[i] = null; - } + /** Lattice values */ + private static final int TOP = 0; + private static final int CONSTANT = 1; + private static final int VARYING = 2; + /** method we're processing */ + private SsaMethod ssaMeth; + /** ssaMeth.getRegCount() */ + private int regCount; + /** Lattice values for each SSA register */ + private int[] latticeValues; + /** For those registers that are constant, this is the constant value */ + private Constant[] latticeConstants; + /** Worklist of basic blocks to be processed */ + private ArrayList<SsaBasicBlock> cfgWorklist; + /** Worklist of executed basic blocks with phis to be processed */ + private ArrayList<SsaBasicBlock> cfgPhiWorklist; + /** Bitset containing bits for each block that has been found executable */ + private BitSet executableBlocks; + /** Worklist for SSA edges. This is a list of registers to process */ + private ArrayList<SsaInsn> ssaWorklist; + /** + * Worklist for SSA edges that represent varying values. It makes the + * algorithm much faster if you move all values to VARYING as fast as + * possible. + */ + private ArrayList<SsaInsn> varyingWorklist; + /** Worklist of potential branches to convert to gotos */ + private ArrayList<SsaInsn> branchWorklist; + + private SCCP(SsaMethod ssaMeth) { + this.ssaMeth = ssaMeth; + this.regCount = ssaMeth.getRegCount(); + this.latticeValues = new int[this.regCount]; + this.latticeConstants = new Constant[this.regCount]; + this.cfgWorklist = new ArrayList<SsaBasicBlock>(); + this.cfgPhiWorklist = new ArrayList<SsaBasicBlock>(); + this.executableBlocks = new BitSet(ssaMeth.getBlocks().size()); + this.ssaWorklist = new ArrayList<SsaInsn>(); + this.varyingWorklist = new ArrayList<SsaInsn>(); + this.branchWorklist = new ArrayList<SsaInsn>(); + for (int i = 0; i < this.regCount; i++) { + latticeValues[i] = TOP; + latticeConstants[i] = null; } - - /** - * Performs sparse conditional constant propagation on a method. - * @param ssaMethod Method to process - */ - public static void process (SsaMethod ssaMethod) { - new SCCP(ssaMethod).run(); + } + + /** + * Performs sparse conditional constant propagation on a method. + * @param ssaMethod Method to process + */ + public static void process(SsaMethod ssaMethod) { + new SCCP(ssaMethod).run(); + } + + /** + * Adds a SSA basic block to the CFG worklist if it's unexecuted, or + * to the CFG phi worklist if it's already executed. + * @param ssaBlock Block to add + */ + private void addBlockToWorklist(SsaBasicBlock ssaBlock) { + if (!executableBlocks.get(ssaBlock.getIndex())) { + cfgWorklist.add(ssaBlock); + executableBlocks.set(ssaBlock.getIndex()); + } else { + cfgPhiWorklist.add(ssaBlock); } - - /** - * Adds a SSA basic block to the CFG worklist if it's unexecuted, or - * to the CFG phi worklist if it's already executed. - * @param ssaBlock Block to add - */ - private void addBlockToWorklist(SsaBasicBlock ssaBlock) { - if (!executableBlocks.get(ssaBlock.getIndex())) { - cfgWorklist.add(ssaBlock); - executableBlocks.set(ssaBlock.getIndex()); - } else { - cfgPhiWorklist.add(ssaBlock); - } + } + + /** + * Adds an SSA register's uses to the SSA worklist. + * @param reg SSA register + * @param latticeValue new lattice value for @param reg. + */ + private void addUsersToWorklist(int reg, int latticeValue) { + if (latticeValue == VARYING) { + for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { + varyingWorklist.add(insn); + } + } else { + for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { + ssaWorklist.add(insn); + } } - - /** - * Adds an SSA register's uses to the SSA worklist. - * @param reg SSA register - * @param latticeValue new lattice value for @param reg. - */ - private void addUsersToWorklist(int reg, int latticeValue) { - if (latticeValue == VARYING) { - for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { - varyingWorklist.add(insn); - } - } else { - for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { - ssaWorklist.add(insn); - } - } + } + + /** + * Sets a lattice value for a register to value. + * @param reg SSA register + * @param value Lattice value + * @param cst Constant value (may be null) + * @return true if the lattice value changed. + */ + private boolean setLatticeValueTo(int reg, int value, Constant cst) { + if (value != CONSTANT) { + if (latticeValues[reg] != value) { + latticeValues[reg] = value; + return true; + } + return false; + } else { + if (latticeValues[reg] != value || !latticeConstants[reg].equals(cst)) { + latticeValues[reg] = value; + latticeConstants[reg] = cst; + return true; + } + return false; + } + } + + /** + * Simulates a PHI node and set the lattice for the result + * to the appropriate value. + * Meet values: + * TOP x anything = TOP + * VARYING x anything = VARYING + * CONSTANT x CONSTANT = CONSTANT if equal constants, VARYING otherwise + * @param insn PHI to simulate. + */ + private void simulatePhi(PhiInsn insn) { + int phiResultReg = insn.getResult().getReg(); + + if (latticeValues[phiResultReg] == VARYING) { + return; } - /** - * Sets a lattice value for a register to value. - * @param reg SSA register - * @param value Lattice value - * @param cst Constant value (may be null) - * @return true if the lattice value changed. - */ - private boolean setLatticeValueTo(int reg, int value, Constant cst) { - if (value != CONSTANT) { - if (latticeValues[reg] != value) { - latticeValues[reg] = value; - return true; - } - return false; - } else { - if (latticeValues[reg] != value - || !latticeConstants[reg].equals(cst)) { - latticeValues[reg] = value; - latticeConstants[reg] = cst; - return true; - } - return false; + RegisterSpecList sources = insn.getSources(); + int phiResultValue = TOP; + Constant phiConstant = null; + int sourceSize = sources.size(); + + for (int i = 0; i < sourceSize; i++) { + int predBlockIndex = insn.predBlockIndexForSourcesIndex(i); + int sourceReg = sources.get(i).getReg(); + int sourceRegValue = latticeValues[sourceReg]; + + if (!executableBlocks.get(predBlockIndex)) { + continue; + } + + if (sourceRegValue == CONSTANT) { + if (phiConstant == null) { + phiConstant = latticeConstants[sourceReg]; + phiResultValue = CONSTANT; + } else if (!latticeConstants[sourceReg].equals(phiConstant)) { + phiResultValue = VARYING; + break; } + } else { + phiResultValue = sourceRegValue; + break; + } } - - /** - * Simulates a PHI node and set the lattice for the result - * to the appropriate value. - * Meet values: - * TOP x anything = TOP - * VARYING x anything = VARYING - * CONSTANT x CONSTANT = CONSTANT if equal constants, VARYING otherwise - * @param insn PHI to simulate. - */ - private void simulatePhi(PhiInsn insn) { - int phiResultReg = insn.getResult().getReg(); - - if (latticeValues[phiResultReg] == VARYING) { - return; + if (setLatticeValueTo(phiResultReg, phiResultValue, phiConstant)) { + addUsersToWorklist(phiResultReg, phiResultValue); + } + } + + /** + * Simulate a block and note the results in the lattice. + * @param block Block to visit + */ + private void simulateBlock(SsaBasicBlock block) { + for (SsaInsn insn : block.getInsns()) { + if (insn instanceof PhiInsn) { + simulatePhi((PhiInsn) insn); + } else { + simulateStmt(insn); + } + } + } + + /** + * Simulate the phis in a block and note the results in the lattice. + * @param block Block to visit + */ + private void simulatePhiBlock(SsaBasicBlock block) { + for (SsaInsn insn : block.getInsns()) { + if (insn instanceof PhiInsn) { + simulatePhi((PhiInsn) insn); + } else { + return; + } + } + } + + /** + * Simulates branch insns, if possible. Adds reachable successor blocks + * to the CFG worklists. + * @param insn branch to simulate + */ + private void simulateBranch(SsaInsn insn) { + Rop opcode = insn.getOpcode(); + RegisterSpecList sources = insn.getSources(); + + boolean constantBranch = false; + boolean constantSuccessor = false; + + // Check if the insn is a branch with a constant condition + if (opcode.getBranchingness() == Rop.BRANCH_IF) { + Constant cA = null; + Constant cB = null; + + RegisterSpec specA = sources.get(0); + int regA = specA.getReg(); + if (!ssaMeth.isRegALocal(specA) && latticeValues[regA] == CONSTANT) { + cA = latticeConstants[regA]; + } + + if (sources.size() == 2) { + RegisterSpec specB = sources.get(1); + int regB = specB.getReg(); + if (!ssaMeth.isRegALocal(specB) && latticeValues[regB] == CONSTANT) { + cB = latticeConstants[regB]; } - - RegisterSpecList sources = insn.getSources(); - int phiResultValue = TOP; - Constant phiConstant = null; - int sourceSize = sources.size(); - - for (int i = 0; i < sourceSize; i++) { - int predBlockIndex = insn.predBlockIndexForSourcesIndex(i); - int sourceReg = sources.get(i).getReg(); - int sourceRegValue = latticeValues[sourceReg]; - - if (!executableBlocks.get(predBlockIndex)) { - continue; + } + + // Calculate the result of the condition + if (cA != null && sources.size() == 1) { + switch (((TypedConstant) cA).getBasicType()) { + case Type.BT_BOOLEAN: { + constantBranch = true; + boolean vA = ((CstBoolean) cA).getValue(); + switch (opcode.getOpcode()) { + case RegOps.IF_EQ: + constantSuccessor = !vA; + break; + case RegOps.IF_NE: + constantSuccessor = vA; + break; + default: + throw new RuntimeException("Unexpected op"); } - - if (sourceRegValue == CONSTANT) { - if (phiConstant == null) { - phiConstant = latticeConstants[sourceReg]; - phiResultValue = CONSTANT; - } else if (!latticeConstants[sourceReg].equals(phiConstant)){ - phiResultValue = VARYING; - break; - } - } else { - phiResultValue = sourceRegValue; + break; + } + case Type.BT_INT: + constantBranch = true; + int vA = ((CstInteger) cA).getValue(); + switch (opcode.getOpcode()) { + case RegOps.IF_EQ: + constantSuccessor = (vA == 0); + break; + case RegOps.IF_NE: + constantSuccessor = (vA != 0); + break; + case RegOps.IF_LT: + constantSuccessor = (vA < 0); + break; + case RegOps.IF_GE: + constantSuccessor = (vA >= 0); break; + case RegOps.IF_LE: + constantSuccessor = (vA <= 0); + break; + case RegOps.IF_GT: + constantSuccessor = (vA > 0); + break; + default: + throw new RuntimeException("Unexpected op"); } + break; + default: + // not yet supported } - if (setLatticeValueTo(phiResultReg, phiResultValue, phiConstant)) { - addUsersToWorklist(phiResultReg, phiResultValue); - } - } - - /** - * Simulate a block and note the results in the lattice. - * @param block Block to visit - */ - private void simulateBlock(SsaBasicBlock block) { - for (SsaInsn insn : block.getInsns()) { - if (insn instanceof PhiInsn) { - simulatePhi((PhiInsn) insn); - } else { - simulateStmt(insn); + } else if (cA != null && cB != null) { + switch (((TypedConstant) cA).getBasicType()) { + case Type.BT_INT: + constantBranch = true; + int vA = ((CstInteger) cA).getValue(); + int vB = ((CstInteger) cB).getValue(); + switch (opcode.getOpcode()) { + case RegOps.IF_EQ: + constantSuccessor = (vA == vB); + break; + case RegOps.IF_NE: + constantSuccessor = (vA != vB); + break; + case RegOps.IF_LT: + constantSuccessor = (vA < vB); + break; + case RegOps.IF_GE: + constantSuccessor = (vA >= vB); + break; + case RegOps.IF_LE: + constantSuccessor = (vA <= vB); + break; + case RegOps.IF_GT: + constantSuccessor = (vA > vB); + break; + default: + throw new RuntimeException("Unexpected op"); } + break; + default: + // not yet supported } + } } - /** - * Simulate the phis in a block and note the results in the lattice. - * @param block Block to visit + /* + * If condition is constant, add only the target block to the + * worklist. Otherwise, add all successors to the worklist. */ - private void simulatePhiBlock(SsaBasicBlock block) { - for (SsaInsn insn : block.getInsns()) { - if (insn instanceof PhiInsn) { - simulatePhi((PhiInsn) insn); - } else { - return; - } - } + SsaBasicBlock block = insn.getBlock(); + + if (constantBranch) { + int successorBlock; + if (constantSuccessor) { + successorBlock = block.getSuccessorList().get(1); + } else { + successorBlock = block.getSuccessorList().get(0); + } + addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock)); + branchWorklist.add(insn); + } else { + for (int i = 0; i < block.getSuccessorList().size(); i++) { + int successorBlock = block.getSuccessorList().get(i); + addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock)); + } + } + } + + /** + * Simulates math insns, if possible. + * + * @param insn non-null insn to simulate + * @param resultType basic type of the result + * @return constant result or null if not simulatable. + */ + private Constant simulateMath(SsaInsn insn, int resultType) { + Insn ropInsn = insn.getOriginalRopInsn(); + int opcode = insn.getOpcode().getOpcode(); + RegisterSpecList sources = insn.getSources(); + int regA = sources.get(0).getReg(); + Constant cA; + Constant cB; + + if (latticeValues[regA] != CONSTANT) { + cA = null; + } else { + cA = latticeConstants[regA]; } - private static String latticeValName(int latticeVal) { - switch (latticeVal) { - case TOP: return "TOP"; - case CONSTANT: return "CONSTANT"; - case VARYING: return "VARYING"; - default: return "UNKNOWN"; - } + if (sources.size() == 1) { + CstInsn cstInsn = (CstInsn) ropInsn; + cB = cstInsn.getConstant(); + } else { /* sources.size() == 2 */ + int regB = sources.get(1).getReg(); + if (latticeValues[regB] != CONSTANT) { + cB = null; + } else { + cB = latticeConstants[regB]; + } } - /** - * Simulates branch insns, if possible. Adds reachable successor blocks - * to the CFG worklists. - * @param insn branch to simulate - */ - private void simulateBranch(SsaInsn insn) { - Rop opcode = insn.getOpcode(); - RegisterSpecList sources = insn.getSources(); + if (cA == null || cB == null) { + //TODO(dx team) handle a constant of 0 with MUL or AND + return null; + } - boolean constantBranch = false; - boolean constantSuccessor = false; + switch (resultType) { + case Type.BT_INT: + int vR; + boolean skip = false; - // Check if the insn is a branch with a constant condition - if (opcode.getBranchingness() == Rop.BRANCH_IF) { - Constant cA = null; - Constant cB = null; + int vA = ((CstInteger) cA).getValue(); + int vB = ((CstInteger) cB).getValue(); - RegisterSpec specA = sources.get(0); - int regA = specA.getReg(); - if (!ssaMeth.isRegALocal(specA) && - latticeValues[regA] == CONSTANT) { - cA = latticeConstants[regA]; + switch (opcode) { + case RegOps.ADD: + vR = vA + vB; + break; + case RegOps.SUB: + // 1 source for reverse sub, 2 sources for regular sub + if (sources.size() == 1) { + vR = vB - vA; + } else { + vR = vA - vB; } - - if (sources.size() == 2) { - RegisterSpec specB = sources.get(1); - int regB = specB.getReg(); - if (!ssaMeth.isRegALocal(specB) && - latticeValues[regB] == CONSTANT) { - cB = latticeConstants[regB]; - } + break; + case RegOps.MUL: + vR = vA * vB; + break; + case RegOps.DIV: + if (vB == 0) { + skip = true; + vR = 0; // just to hide a warning + } else { + vR = vA / vB; } - - // Calculate the result of the condition - if (cA != null && sources.size() == 1) { - switch (((TypedConstant) cA).getBasicType()) { - case Type.BT_BOOLEAN: { - constantBranch = true; - boolean vA = ((CstBoolean) cA).getValue(); - switch (opcode.getOpcode()) { - case RegOps.IF_EQ: - constantSuccessor = !vA; - break; - case RegOps.IF_NE: - constantSuccessor = vA; - break; - default: - throw new RuntimeException("Unexpected op"); - } - break; - } - case Type.BT_INT: - constantBranch = true; - int vA = ((CstInteger) cA).getValue(); - switch (opcode.getOpcode()) { - case RegOps.IF_EQ: - constantSuccessor = (vA == 0); - break; - case RegOps.IF_NE: - constantSuccessor = (vA != 0); - break; - case RegOps.IF_LT: - constantSuccessor = (vA < 0); - break; - case RegOps.IF_GE: - constantSuccessor = (vA >= 0); - break; - case RegOps.IF_LE: - constantSuccessor = (vA <= 0); - break; - case RegOps.IF_GT: - constantSuccessor = (vA > 0); - break; - default: - throw new RuntimeException("Unexpected op"); - } - break; - default: - // not yet supported - } - } else if (cA != null && cB != null) { - switch (((TypedConstant) cA).getBasicType()) { - case Type.BT_INT: - constantBranch = true; - int vA = ((CstInteger) cA).getValue(); - int vB = ((CstInteger) cB).getValue(); - switch (opcode.getOpcode()) { - case RegOps.IF_EQ: - constantSuccessor = (vA == vB); - break; - case RegOps.IF_NE: - constantSuccessor = (vA != vB); - break; - case RegOps.IF_LT: - constantSuccessor = (vA < vB); - break; - case RegOps.IF_GE: - constantSuccessor = (vA >= vB); - break; - case RegOps.IF_LE: - constantSuccessor = (vA <= vB); - break; - case RegOps.IF_GT: - constantSuccessor = (vA > vB); - break; - default: - throw new RuntimeException("Unexpected op"); - } - break; - default: - // not yet supported - } + break; + case RegOps.AND: + vR = vA & vB; + break; + case RegOps.OR: + vR = vA | vB; + break; + case RegOps.XOR: + vR = vA ^ vB; + break; + case RegOps.SHL: + vR = vA << vB; + break; + case RegOps.SHR: + vR = vA >> vB; + break; + case RegOps.USHR: + vR = vA >>> vB; + break; + case RegOps.REM: + if (vB == 0) { + skip = true; + vR = 0; // just to hide a warning + } else { + vR = vA % vB; } + break; + default: + throw new RuntimeException("Unexpected op"); } - /* - * If condition is constant, add only the target block to the - * worklist. Otherwise, add all successors to the worklist. - */ - SsaBasicBlock block = insn.getBlock(); + return skip ? null : CstInteger.make(vR); - if (constantBranch) { - int successorBlock; - if (constantSuccessor) { - successorBlock = block.getSuccessorList().get(1); - } else { - successorBlock = block.getSuccessorList().get(0); - } - addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock)); - branchWorklist.add(insn); - } else { - for (int i = 0; i < block.getSuccessorList().size(); i++) { - int successorBlock = block.getSuccessorList().get(i); - addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock)); - } - } + default: + // not yet supported + return null; + } + } + + /** + * Simulates a statement and set the result lattice value. + * @param insn instruction to simulate + */ + private void simulateStmt(SsaInsn insn) { + Insn ropInsn = insn.getOriginalRopInsn(); + if (ropInsn.getOpcode().getBranchingness() != Rop.BRANCH_NONE + || ropInsn.getOpcode().isCallLike()) { + simulateBranch(insn); } - /** - * Simulates math insns, if possible. - * - * @param insn non-null insn to simulate - * @param resultType basic type of the result - * @return constant result or null if not simulatable. - */ - private Constant simulateMath(SsaInsn insn, int resultType) { - Insn ropInsn = insn.getOriginalRopInsn(); - int opcode = insn.getOpcode().getOpcode(); - RegisterSpecList sources = insn.getSources(); - int regA = sources.get(0).getReg(); - Constant cA; - Constant cB; - - if (latticeValues[regA] != CONSTANT) { - cA = null; - } else { - cA = latticeConstants[regA]; - } + int opcode = insn.getOpcode().getOpcode(); + RegisterSpec result = insn.getResult(); + + if (result == null) { + // Find move-result-pseudo result for int div and int rem + if (opcode == RegOps.DIV || opcode == RegOps.REM) { + SsaBasicBlock succ = insn.getBlock().getPrimarySuccessor(); + result = succ.getInsns().get(0).getResult(); + } else { + return; + } + } - if (sources.size() == 1) { - CstInsn cstInsn = (CstInsn) ropInsn; - cB = cstInsn.getConstant(); - } else { /* sources.size() == 2 */ - int regB = sources.get(1).getReg(); - if (latticeValues[regB] != CONSTANT) { - cB = null; - } else { - cB = latticeConstants[regB]; - } + int resultReg = result.getReg(); + int resultValue = VARYING; + Constant resultConstant = null; + + switch (opcode) { + case RegOps.CONST: { + CstInsn cstInsn = (CstInsn) ropInsn; + resultValue = CONSTANT; + resultConstant = cstInsn.getConstant(); + break; + } + case RegOps.MOVE: { + if (insn.getSources().size() == 1) { + int sourceReg = insn.getSources().get(0).getReg(); + resultValue = latticeValues[sourceReg]; + resultConstant = latticeConstants[sourceReg]; } - - if (cA == null || cB == null) { - //TODO handle a constant of 0 with MUL or AND - return null; + break; + } + case RegOps.ADD: + case RegOps.SUB: + case RegOps.MUL: + case RegOps.DIV: + case RegOps.AND: + case RegOps.OR: + case RegOps.XOR: + case RegOps.SHL: + case RegOps.SHR: + case RegOps.USHR: + case RegOps.REM: { + resultConstant = simulateMath(insn, result.getBasicType()); + if (resultConstant != null) { + resultValue = CONSTANT; } - - switch (resultType) { - case Type.BT_INT: - int vR; - boolean skip=false; - - int vA = ((CstInteger) cA).getValue(); - int vB = ((CstInteger) cB).getValue(); - - switch (opcode) { - case RegOps.ADD: - vR = vA + vB; - break; - case RegOps.SUB: - // 1 source for reverse sub, 2 sources for regular sub - if (sources.size() == 1) { - vR = vB - vA; - } else { - vR = vA - vB; - } - break; - case RegOps.MUL: - vR = vA * vB; - break; - case RegOps.DIV: - if (vB == 0) { - skip = true; - vR = 0; // just to hide a warning - } else { - vR = vA / vB; - } - break; - case RegOps.AND: - vR = vA & vB; - break; - case RegOps.OR: - vR = vA | vB; - break; - case RegOps.XOR: - vR = vA ^ vB; - break; - case RegOps.SHL: - vR = vA << vB; - break; - case RegOps.SHR: - vR = vA >> vB; - break; - case RegOps.USHR: - vR = vA >>> vB; - break; - case RegOps.REM: - if (vB == 0) { - skip = true; - vR = 0; // just to hide a warning - } else { - vR = vA % vB; - } - break; - default: - throw new RuntimeException("Unexpected op"); - } - - return skip ? null : CstInteger.make(vR); - - default: - // not yet supported - return null; + break; + } + case RegOps.MOVE_RESULT_PSEUDO: { + if (latticeValues[resultReg] == CONSTANT) { + resultValue = latticeValues[resultReg]; + resultConstant = latticeConstants[resultReg]; } + break; + } + // TODO(dx team): Handle non-int arithmetic. + // TODO(dx team): Eliminate check casts that we can prove the type of. + default: { + } } - - /** - * Simulates a statement and set the result lattice value. - * @param insn instruction to simulate - */ - private void simulateStmt(SsaInsn insn) { - Insn ropInsn = insn.getOriginalRopInsn(); - if (ropInsn.getOpcode().getBranchingness() != Rop.BRANCH_NONE - || ropInsn.getOpcode().isCallLike()) { - simulateBranch(insn); + if (setLatticeValueTo(resultReg, resultValue, resultConstant)) { + addUsersToWorklist(resultReg, resultValue); + } + } + + private void run() { + SsaBasicBlock firstBlock = ssaMeth.getEntryBlock(); + addBlockToWorklist(firstBlock); + + /* Empty all the worklists by propagating our values */ + while (!cfgWorklist.isEmpty() || !cfgPhiWorklist.isEmpty() || !ssaWorklist.isEmpty() + || !varyingWorklist.isEmpty()) { + while (!cfgWorklist.isEmpty()) { + int listSize = cfgWorklist.size() - 1; + SsaBasicBlock block = cfgWorklist.remove(listSize); + simulateBlock(block); + } + + while (!cfgPhiWorklist.isEmpty()) { + int listSize = cfgPhiWorklist.size() - 1; + SsaBasicBlock block = cfgPhiWorklist.remove(listSize); + simulatePhiBlock(block); + } + + while (!varyingWorklist.isEmpty()) { + int listSize = varyingWorklist.size() - 1; + SsaInsn insn = varyingWorklist.remove(listSize); + + if (!executableBlocks.get(insn.getBlock().getIndex())) { + continue; } - int opcode = insn.getOpcode().getOpcode(); - RegisterSpec result = insn.getResult(); - - if (result == null) { - // Find move-result-pseudo result for int div and int rem - if (opcode == RegOps.DIV || opcode == RegOps.REM) { - SsaBasicBlock succ = insn.getBlock().getPrimarySuccessor(); - result = succ.getInsns().get(0).getResult(); - } else { - return; - } + if (insn instanceof PhiInsn) { + simulatePhi((PhiInsn) insn); + } else { + simulateStmt(insn); } + } + while (!ssaWorklist.isEmpty()) { + int listSize = ssaWorklist.size() - 1; + SsaInsn insn = ssaWorklist.remove(listSize); - int resultReg = result.getReg(); - int resultValue = VARYING; - Constant resultConstant = null; - - switch (opcode) { - case RegOps.CONST: { - CstInsn cstInsn = (CstInsn)ropInsn; - resultValue = CONSTANT; - resultConstant = cstInsn.getConstant(); - break; - } - case RegOps.MOVE: { - if (insn.getSources().size() == 1) { - int sourceReg = insn.getSources().get(0).getReg(); - resultValue = latticeValues[sourceReg]; - resultConstant = latticeConstants[sourceReg]; - } - break; - } - case RegOps.ADD: - case RegOps.SUB: - case RegOps.MUL: - case RegOps.DIV: - case RegOps.AND: - case RegOps.OR: - case RegOps.XOR: - case RegOps.SHL: - case RegOps.SHR: - case RegOps.USHR: - case RegOps.REM: { - resultConstant = simulateMath(insn, result.getBasicType()); - if (resultConstant != null) { - resultValue = CONSTANT; - } - break; - } - case RegOps.MOVE_RESULT_PSEUDO: { - if (latticeValues[resultReg] == CONSTANT) { - resultValue = latticeValues[resultReg]; - resultConstant = latticeConstants[resultReg]; - } - break; - } - // TODO: Handle non-int arithmetic. - // TODO: Eliminate check casts that we can prove the type of. - default: {} + if (!executableBlocks.get(insn.getBlock().getIndex())) { + continue; } - if (setLatticeValueTo(resultReg, resultValue, resultConstant)) { - addUsersToWorklist(resultReg, resultValue); - } - } - - private void run() { - SsaBasicBlock firstBlock = ssaMeth.getEntryBlock(); - addBlockToWorklist(firstBlock); - - /* Empty all the worklists by propagating our values */ - while (!cfgWorklist.isEmpty() - || !cfgPhiWorklist.isEmpty() - || !ssaWorklist.isEmpty() - || !varyingWorklist.isEmpty()) { - while (!cfgWorklist.isEmpty()) { - int listSize = cfgWorklist.size() - 1; - SsaBasicBlock block = cfgWorklist.remove(listSize); - simulateBlock(block); - } - - while (!cfgPhiWorklist.isEmpty()) { - int listSize = cfgPhiWorklist.size() - 1; - SsaBasicBlock block = cfgPhiWorklist.remove(listSize); - simulatePhiBlock(block); - } - while (!varyingWorklist.isEmpty()) { - int listSize = varyingWorklist.size() - 1; - SsaInsn insn = varyingWorklist.remove(listSize); - - if (!executableBlocks.get(insn.getBlock().getIndex())) { - continue; - } - - if (insn instanceof PhiInsn) { - simulatePhi((PhiInsn)insn); - } else { - simulateStmt(insn); - } - } - while (!ssaWorklist.isEmpty()) { - int listSize = ssaWorklist.size() - 1; - SsaInsn insn = ssaWorklist.remove(listSize); - - if (!executableBlocks.get(insn.getBlock().getIndex())) { - continue; - } - - if (insn instanceof PhiInsn) { - simulatePhi((PhiInsn)insn); - } else { - simulateStmt(insn); - } - } + if (insn instanceof PhiInsn) { + simulatePhi((PhiInsn) insn); + } else { + simulateStmt(insn); } - - replaceConstants(); - replaceBranches(); + } } - /** - * Replaces TypeBearers in source register specs with constant type - * bearers if possible. These are then referenced in later optimization - * steps. - */ - private void replaceConstants() { - for (int reg = 0; reg < regCount; reg++) { - if (latticeValues[reg] != CONSTANT) { - continue; - } - if (!(latticeConstants[reg] instanceof TypedConstant)) { - // We can't do much with these - continue; - } - - SsaInsn defn = ssaMeth.getDefinitionForRegister(reg); - TypeBearer typeBearer = defn.getResult().getTypeBearer(); - - if (typeBearer.isConstant()) { - /* - * The definition was a constant already. - * The uses should be as well. - */ - continue; - } - - // Update the destination RegisterSpec with the constant value - RegisterSpec dest = defn.getResult(); - RegisterSpec newDest - = dest.withType((TypedConstant)latticeConstants[reg]); - defn.setResult(newDest); - - /* - * Update the sources RegisterSpec's of all non-move uses. - * These will be used in later steps. - */ - for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { - if (insn.isPhiOrMove()) { - continue; - } + replaceConstants(); + replaceBranches(); + } + + /** + * Replaces TypeBearers in source register specs with constant type + * bearers if possible. These are then referenced in later optimization + * steps. + */ + private void replaceConstants() { + for (int reg = 0; reg < regCount; reg++) { + if (latticeValues[reg] != CONSTANT) { + continue; + } + if (!(latticeConstants[reg] instanceof TypedConstant)) { + // We can't do much with these + continue; + } + + SsaInsn defn = ssaMeth.getDefinitionForRegister(reg); + TypeBearer typeBearer = defn.getResult().getTypeBearer(); + + if (typeBearer.isConstant()) { + /* + * The definition was a constant already. + * The uses should be as well. + */ + continue; + } + + // Update the destination RegisterSpec with the constant value + RegisterSpec dest = defn.getResult(); + RegisterSpec newDest = dest.withType((TypedConstant) latticeConstants[reg]); + defn.setResult(newDest); + + /* + * Update the sources RegisterSpec's of all non-move uses. + * These will be used in later steps. + */ + for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { + if (insn.isPhiOrMove()) { + continue; + } - NormalSsaInsn nInsn = (NormalSsaInsn) insn; - RegisterSpecList sources = insn.getSources(); + NormalSsaInsn nInsn = (NormalSsaInsn) insn; + RegisterSpecList sources = insn.getSources(); - int index = sources.indexOfRegister(reg); + int index = sources.indexOfRegister(reg); - RegisterSpec spec = sources.get(index); - RegisterSpec newSpec - = spec.withType((TypedConstant)latticeConstants[reg]); + RegisterSpec spec = sources.get(index); + RegisterSpec newSpec = spec.withType((TypedConstant) latticeConstants[reg]); - nInsn.changeOneSource(index, newSpec); - } - } + nInsn.changeOneSource(index, newSpec); + } } - - /** - * Replaces branches that have constant conditions with gotos - */ - private void replaceBranches() { - for (SsaInsn insn : branchWorklist) { - // Find if a successor block is never executed - int oldSuccessor = -1; - SsaBasicBlock block = insn.getBlock(); - int successorSize = block.getSuccessorList().size(); - for (int i = 0; i < successorSize; i++) { - int successorBlock = block.getSuccessorList().get(i); - if (!executableBlocks.get(successorBlock)) { - oldSuccessor = successorBlock; - } - } - - /* - * Prune branches that have already been handled and ones that no - * longer have constant conditions (no nonexecutable successors) - */ - if (successorSize != 2 || oldSuccessor == -1) continue; - - // Replace branch with goto - Insn originalRopInsn = insn.getOriginalRopInsn(); - block.replaceLastInsn(new PlainInsn(Rops.GOTO, - originalRopInsn.getPosition(), null, RegisterSpecList.EMPTY)); - block.removeSuccessor(oldSuccessor); + } + + /** + * Replaces branches that have constant conditions with gotos + */ + private void replaceBranches() { + for (SsaInsn insn : branchWorklist) { + // Find if a successor block is never executed + int oldSuccessor = -1; + SsaBasicBlock block = insn.getBlock(); + int successorSize = block.getSuccessorList().size(); + for (int i = 0; i < successorSize; i++) { + int successorBlock = block.getSuccessorList().get(i); + if (!executableBlocks.get(successorBlock)) { + oldSuccessor = successorBlock; } + } + + /* + * Prune branches that have already been handled and ones that no + * longer have constant conditions (no nonexecutable successors) + */ + if (successorSize != 2 || oldSuccessor == -1) { + continue; + } + + // Replace branch with goto + Insn originalRopInsn = insn.getOriginalRopInsn(); + block.replaceLastInsn( + new PlainInsn(Rops.GOTO, originalRopInsn.getPosition(), null, RegisterSpecList.EMPTY)); + block.removeSuccessor(oldSuccessor); } + } } diff --git a/dx/src/com/android/jack/dx/ssa/SetFactory.java b/dx/src/com/android/jack/dx/ssa/SetFactory.java index 9c24b8b..0d92d20 100644 --- a/dx/src/com/android/jack/dx/ssa/SetFactory.java +++ b/dx/src/com/android/jack/dx/ssa/SetFactory.java @@ -26,70 +26,65 @@ import com.android.jack.dx.util.ListIntSet; */ public final class SetFactory { - /** - * BitIntSet/ListIntSet threshold for dominance frontier sets. These - * sets are kept per basic block until phi placement and tend to be, - * like the CFG itself, very sparse at large sizes. - * - * A value of 3072 here is somewhere around 1.125mb of total bitset size. - */ - private static final int DOMFRONT_SET_THRESHOLD_SIZE = 3072; + /** + * BitIntSet/ListIntSet threshold for dominance frontier sets. These + * sets are kept per basic block until phi placement and tend to be, + * like the CFG itself, very sparse at large sizes. + * + * A value of 3072 here is somewhere around 1.125mb of total bitset size. + */ + private static final int DOMFRONT_SET_THRESHOLD_SIZE = 3072; - /** - * BitIntSet/ListIntSet threshold for interference graph sets. These - * sets are kept per register until register allocation is done. - * - * A value of 3072 here is somewhere around 1.125mb of total bitset size. - */ - private static final int INTERFERENCE_SET_THRESHOLD_SIZE = 3072; + /** + * BitIntSet/ListIntSet threshold for interference graph sets. These + * sets are kept per register until register allocation is done. + * + * A value of 3072 here is somewhere around 1.125mb of total bitset size. + */ + private static final int INTERFERENCE_SET_THRESHOLD_SIZE = 3072; - /** - * BitIntSet/ListIntSet threshold for the live in/out sets kept by - * {@link SsaBasicBlock}. These are sets of SSA registers kept per basic - * block during register allocation. - * - * The total size of a bitset for this would be the count of blocks - * times the size of registers. The threshold value here is merely - * the register count, which is typically on the order of the block - * count as well. - */ - private static final int LIVENESS_SET_THRESHOLD_SIZE = 3072; + /** + * BitIntSet/ListIntSet threshold for the live in/out sets kept by + * {@link SsaBasicBlock}. These are sets of SSA registers kept per basic + * block during register allocation. + * + * The total size of a bitset for this would be the count of blocks + * times the size of registers. The threshold value here is merely + * the register count, which is typically on the order of the block + * count as well. + */ + private static final int LIVENESS_SET_THRESHOLD_SIZE = 3072; - /** - * Make IntSet for the dominance-frontier sets. - * - * @param szBlocks {@code >=0;} count of basic blocks in method - * @return {@code non-null;} appropriate set - */ - /*package*/ static IntSet makeDomFrontSet(int szBlocks) { - return szBlocks <= DOMFRONT_SET_THRESHOLD_SIZE - ? new BitIntSet(szBlocks) - : new ListIntSet(); - } + /** + * Make IntSet for the dominance-frontier sets. + * + * @param szBlocks {@code >=0;} count of basic blocks in method + * @return {@code non-null;} appropriate set + */ + /*package*/static IntSet makeDomFrontSet(int szBlocks) { + return szBlocks <= DOMFRONT_SET_THRESHOLD_SIZE ? new BitIntSet(szBlocks) : new ListIntSet(); + } - /** - * Make IntSet for the interference graph sets. Public because - * InterferenceGraph is in another package. - * - * @param countRegs {@code >=0;} count of SSA registers used in method - * @return {@code non-null;} appropriate set - */ - public static IntSet makeInterferenceSet(int countRegs) { - return countRegs <= INTERFERENCE_SET_THRESHOLD_SIZE - ? new BitIntSet(countRegs) - : new ListIntSet(); - } + /** + * Make IntSet for the interference graph sets. Public because + * InterferenceGraph is in another package. + * + * @param countRegs {@code >=0;} count of SSA registers used in method + * @return {@code non-null;} appropriate set + */ + public static IntSet makeInterferenceSet(int countRegs) { + return countRegs <= INTERFERENCE_SET_THRESHOLD_SIZE ? new BitIntSet(countRegs) + : new ListIntSet(); + } - /** - * Make IntSet for register live in/out sets. - * - * @param countRegs {@code >=0;} count of SSA registers used in method - * @return {@code non-null;} appropriate set - */ - /*package*/ static IntSet makeLivenessSet(int countRegs) { - return countRegs <= LIVENESS_SET_THRESHOLD_SIZE - ? new BitIntSet(countRegs) - : new ListIntSet(); - } + /** + * Make IntSet for register live in/out sets. + * + * @param countRegs {@code >=0;} count of SSA registers used in method + * @return {@code non-null;} appropriate set + */ + /*package*/static IntSet makeLivenessSet(int countRegs) { + return countRegs <= LIVENESS_SET_THRESHOLD_SIZE ? new BitIntSet(countRegs) : new ListIntSet(); + } } diff --git a/dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java b/dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java index 4217184..2d16474 100644 --- a/dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java +++ b/dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java @@ -41,992 +41,962 @@ import java.util.List; * An SSA representation of a basic block. */ public final class SsaBasicBlock { - /** - * {@code non-null;} comparator for instances of this class that - * just compares block labels - */ - public static final Comparator<SsaBasicBlock> LABEL_COMPARATOR = - new LabelComparator(); - - /** {@code non-null;} insn list associated with this instance */ - private ArrayList<SsaInsn> insns; - - /** {@code non-null;} predecessor set (by block list index) */ - private BitSet predecessors; - - /** {@code non-null;} successor set (by block list index) */ - private BitSet successors; - - /** - * {@code non-null;} ordered successor list - * (same block may be listed more than once) - */ - private IntList successorList; - - /** - * block list index of primary successor, or {@code -1} for no primary - * successor - */ - private int primarySuccessor = -1; - - /** label of block in rop form */ - private int ropLabel; - - /** {@code non-null;} method we belong to */ - private SsaMethod parent; - - /** our index into parent.getBlock() */ - private int index; - - /** list of dom children */ - private final ArrayList<SsaBasicBlock> domChildren; - - /** - * the number of moves added to the end of the block during the - * phi-removal process. Retained for subsequent move scheduling. - */ - private int movesFromPhisAtEnd = 0; - - /** - * the number of moves added to the beginning of the block during the - * phi-removal process. Retained for subsequent move scheduling. - */ - private int movesFromPhisAtBeginning = 0; - - /** - * contains last computed value of reachability of this block, or -1 - * if reachability hasn't been calculated yet - */ - private int reachable = -1; - - /** - * {@code null-ok;} indexed by reg: the regs that are live-in at - * this block - */ - private IntSet liveIn; - - /** - * {@code null-ok;} indexed by reg: the regs that are live-out at - * this block - */ - private IntSet liveOut; - - /** - * Creates a new empty basic block. - * - * @param basicBlockIndex index this block will have - * @param ropLabel original rop-form label - * @param parent method of this block - */ - public SsaBasicBlock(final int basicBlockIndex, final int ropLabel, - final SsaMethod parent) { - this.parent = parent; - this.index = basicBlockIndex; - this.insns = new ArrayList<SsaInsn>(); - this.ropLabel = ropLabel; - - this.predecessors = new BitSet(parent.getBlocks().size()); - this.successors = new BitSet(parent.getBlocks().size()); - this.successorList = new IntList(); - - domChildren = new ArrayList<SsaBasicBlock>(); - } - - /** - * Creates a new SSA basic block from a ROP form basic block. - * - * @param rmeth original method - * @param basicBlockIndex index this block will have - * @param parent method of this block predecessor set will be - * updated - * @return new instance - */ - public static SsaBasicBlock newFromRop(RopMethod rmeth, - int basicBlockIndex, final SsaMethod parent) { - BasicBlockList ropBlocks = rmeth.getBlocks(); - BasicBlock bb = ropBlocks.get(basicBlockIndex); - SsaBasicBlock result = - new SsaBasicBlock(basicBlockIndex, bb.getLabel(), parent); - InsnList ropInsns = bb.getInsns(); - - result.insns.ensureCapacity(ropInsns.size()); - - for (int i = 0, sz = ropInsns.size() ; i < sz ; i++) { - result.insns.add(new NormalSsaInsn (ropInsns.get(i), result)); - } - - result.predecessors = SsaMethod.bitSetFromLabelList( - ropBlocks, - rmeth.labelToPredecessors(bb.getLabel())); - - result.successors - = SsaMethod.bitSetFromLabelList(ropBlocks, bb.getSuccessors()); - - result.successorList - = SsaMethod.indexListFromLabelList(ropBlocks, - bb.getSuccessors()); - if (result.successorList.size() != 0) { - int primarySuccessor = bb.getPrimarySuccessor(); - - result.primarySuccessor = (primarySuccessor < 0) - ? -1 : ropBlocks.indexOfLabel(primarySuccessor); - } - - return result; - } - - /** - * Adds a basic block as a dom child for this block. Used when constructing - * the dom tree. - * - * @param child {@code non-null;} new dom child - */ - public void addDomChild(SsaBasicBlock child) { - domChildren.add(child); - } - - /** - * Gets the dom children for this node. Don't modify this list. - * - * @return {@code non-null;} list of dom children - */ - public ArrayList<SsaBasicBlock> getDomChildren() { - return domChildren; - } - - /** - * Adds a phi insn to the beginning of this block. The result type of - * the phi will be set to void, to indicate that it's currently unknown. - * - * @param reg {@code >=0;} result reg - */ - public void addPhiInsnForReg(int reg) { - insns.add(0, new PhiInsn(reg, this)); - } - - /** - * Adds a phi insn to the beginning of this block. This is to be used - * when the result type or local-association can be determined at phi - * insert time. - * - * @param resultSpec {@code non-null;} reg - */ - public void addPhiInsnForReg(RegisterSpec resultSpec) { - insns.add(0, new PhiInsn(resultSpec, this)); - } - - /** - * Adds an insn to the head of this basic block, just after any phi - * insns. - * - * @param insn {@code non-null;} rop-form insn to add - */ - public void addInsnToHead(Insn insn) { - SsaInsn newInsn = SsaInsn.makeFromRop(insn, this); - insns.add(getCountPhiInsns(), newInsn); - parent.onInsnAdded(newInsn); - } - - /** - * Replaces the last insn in this block. The provided insn must have - * some branchingness. - * - * @param insn {@code non-null;} rop-form insn to add, which must branch. - */ - public void replaceLastInsn(Insn insn) { - if (insn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) { - throw new IllegalArgumentException("last insn must branch"); - } - - SsaInsn oldInsn = insns.get(insns.size() - 1); - SsaInsn newInsn = SsaInsn.makeFromRop(insn, this); - - insns.set(insns.size() - 1, newInsn); - - parent.onInsnRemoved(oldInsn); - parent.onInsnAdded(newInsn); - } - - /** - * Visits each phi insn. - * - * @param v {@code non-null;} the callback - */ - public void forEachPhiInsn(PhiInsn.Visitor v) { - int sz = insns.size(); - - for (int i = 0; i < sz; i++) { - SsaInsn insn = insns.get(i); - if (insn instanceof PhiInsn) { - v.visitPhiInsn((PhiInsn) insn); - } else { - /* - * Presently we assume PhiInsn's are in a continuous - * block at the top of the list - */ - break; - } - } - } - - /** - * Deletes all phi insns. Do this after adding appropriate move insns. - */ - public void removeAllPhiInsns() { + public static boolean enablePhisBeforeMoveException = false; + + /** + * {@code non-null;} comparator for instances of this class that + * just compares block labels + */ + public static final Comparator<SsaBasicBlock> LABEL_COMPARATOR = new LabelComparator(); + + /** {@code non-null;} insn list associated with this instance */ + private ArrayList<SsaInsn> insns; + + /** {@code non-null;} predecessor set (by block list index) */ + private BitSet predecessors; + + /** {@code non-null;} successor set (by block list index) */ + private BitSet successors; + + /** + * {@code non-null;} ordered successor list + * (same block may be listed more than once) + */ + private IntList successorList; + + /** + * block list index of primary successor, or {@code -1} for no primary + * successor + */ + private int primarySuccessor = -1; + + /** label of block in rop form */ + private int ropLabel; + + /** {@code non-null;} method we belong to */ + private SsaMethod parent; + + /** our index into parent.getBlock() */ + private int index; + + /** list of dom children */ + private final ArrayList<SsaBasicBlock> domChildren; + + /** + * the number of moves added to the end of the block during the + * phi-removal process. Retained for subsequent move scheduling. + */ + private int movesFromPhisAtEnd = 0; + + /** + * the number of moves added to the beginning of the block during the + * phi-removal process. Retained for subsequent move scheduling. + */ + private int movesFromPhisAtBeginning = 0; + + /** + * contains last computed value of reachability of this block, or -1 + * if reachability hasn't been calculated yet + */ + private int reachable = -1; + + /** + * {@code null-ok;} indexed by reg: the regs that are live-in at + * this block + */ + private IntSet liveIn; + + /** + * {@code null-ok;} indexed by reg: the regs that are live-out at + * this block + */ + private IntSet liveOut; + + /** + * Creates a new empty basic block. + * + * @param basicBlockIndex index this block will have + * @param ropLabel original rop-form label + * @param parent method of this block + */ + public SsaBasicBlock(final int basicBlockIndex, final int ropLabel, final SsaMethod parent) { + this.parent = parent; + this.index = basicBlockIndex; + this.insns = new ArrayList<SsaInsn>(); + this.ropLabel = ropLabel; + + this.predecessors = new BitSet(parent.getBlocks().size()); + this.successors = new BitSet(parent.getBlocks().size()); + this.successorList = new IntList(); + + domChildren = new ArrayList<SsaBasicBlock>(); + } + + /** + * Creates a new SSA basic block from a ROP form basic block. + * + * @param rmeth original method + * @param basicBlockIndex index this block will have + * @param parent method of this block predecessor set will be + * updated + * @return new instance + */ + public static SsaBasicBlock newFromRop(RopMethod rmeth, int basicBlockIndex, + final SsaMethod parent) { + BasicBlockList ropBlocks = rmeth.getBlocks(); + BasicBlock bb = ropBlocks.get(basicBlockIndex); + SsaBasicBlock result = new SsaBasicBlock(basicBlockIndex, bb.getLabel(), parent); + InsnList ropInsns = bb.getInsns(); + + result.insns.ensureCapacity(ropInsns.size()); + + for (int i = 0, sz = ropInsns.size(); i < sz; i++) { + result.insns.add(new NormalSsaInsn(ropInsns.get(i), result)); + } + + result.predecessors = + SsaMethod.bitSetFromLabelList(ropBlocks, rmeth.labelToPredecessors(bb.getLabel())); + + result.successors = SsaMethod.bitSetFromLabelList(ropBlocks, bb.getSuccessors()); + + result.successorList = SsaMethod.indexListFromLabelList(ropBlocks, bb.getSuccessors()); + + if (result.successorList.size() != 0) { + int primarySuccessor = bb.getPrimarySuccessor(); + + result.primarySuccessor = + (primarySuccessor < 0) ? -1 : ropBlocks.indexOfLabel(primarySuccessor); + } + + return result; + } + + /** + * Adds a basic block as a dom child for this block. Used when constructing + * the dom tree. + * + * @param child {@code non-null;} new dom child + */ + public void addDomChild(SsaBasicBlock child) { + domChildren.add(child); + } + + /** + * Gets the dom children for this node. Don't modify this list. + * + * @return {@code non-null;} list of dom children + */ + public ArrayList<SsaBasicBlock> getDomChildren() { + return domChildren; + } + + /** + * Adds a phi insn to the beginning of this block. The result type of + * the phi will be set to void, to indicate that it's currently unknown. + * + * @param reg {@code >=0;} result reg + */ + public void addPhiInsnForReg(int reg) { + insns.add(0, new PhiInsn(reg, this)); + } + + /** + * Adds a phi insn to the beginning of this block. This is to be used + * when the result type or local-association can be determined at phi + * insert time. + * + * @param resultSpec {@code non-null;} reg + */ + public void addPhiInsnForReg(RegisterSpec resultSpec) { + insns.add(0, new PhiInsn(resultSpec, this)); + } + + /** + * Adds an insn to the head of this basic block, just after any phi + * insns. + * + * @param insn {@code non-null;} rop-form insn to add + */ + public void addInsnToHead(Insn insn) { + SsaInsn newInsn = SsaInsn.makeFromRop(insn, this); + insns.add(getCountPhiInsns(), newInsn); + parent.onInsnAdded(newInsn); + } + + /** + * Replaces the last insn in this block. The provided insn must have + * some branchingness. + * + * @param insn {@code non-null;} rop-form insn to add, which must branch. + */ + public void replaceLastInsn(Insn insn) { + if (insn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) { + throw new IllegalArgumentException("last insn must branch"); + } + + SsaInsn oldInsn = insns.get(insns.size() - 1); + SsaInsn newInsn = SsaInsn.makeFromRop(insn, this); + + insns.set(insns.size() - 1, newInsn); + + parent.onInsnRemoved(oldInsn); + parent.onInsnAdded(newInsn); + } + + /** + * Visits each phi insn. + * + * @param v {@code non-null;} the callback + */ + public void forEachPhiInsn(PhiInsn.Visitor v) { + int sz = insns.size(); + + for (int i = 0; i < sz; i++) { + SsaInsn insn = insns.get(i); + if (insn instanceof PhiInsn) { + v.visitPhiInsn((PhiInsn) insn); + } else { /* * Presently we assume PhiInsn's are in a continuous - * block at the top of the list. + * block at the top of the list */ - - insns.subList(0, getCountPhiInsns()).clear(); - } - - /** - * Gets the number of phi insns at the top of this basic block. - * - * @return count of phi insns - */ - private int getCountPhiInsns() { - int countPhiInsns; - - int sz = insns.size(); - for (countPhiInsns = 0; countPhiInsns < sz; countPhiInsns++) { - SsaInsn insn = insns.get(countPhiInsns); - if (!(insn instanceof PhiInsn)) { - break; - } - } - - return countPhiInsns; - } - - /** - * @return {@code non-null;} the (mutable) instruction list for this block, - * with phi insns at the beginning - */ - public ArrayList<SsaInsn> getInsns() { - return insns; - } - - /** - * @return {@code non-null;} the (mutable) list of phi insns for this block - */ - public List<SsaInsn> getPhiInsns() { - return insns.subList(0, getCountPhiInsns()); - } - - /** - * @return the block index of this block - */ - public int getIndex() { - return index; - } - - /** - * @return the label of this block in rop form - */ - public int getRopLabel() { - return ropLabel; - } - - /** - * @return the label of this block in rop form as a hex string - */ - public String getRopLabelString() { - return Hex.u2(ropLabel); - } - - /** - * @return {@code non-null;} predecessors set, indexed by block index - */ - public BitSet getPredecessors() { - return predecessors; - } - - /** - * @return {@code non-null;} successors set, indexed by block index - */ - public BitSet getSuccessors() { - return successors; - } - - /** - * @return {@code non-null;} ordered successor list, containing block - * indicies - */ - public IntList getSuccessorList() { - return successorList; - } - - /** - * @return {@code >= -1;} block index of primary successor or - * {@code -1} if no primary successor - */ - public int getPrimarySuccessorIndex() { - return primarySuccessor; - } - - /** - * @return rop label of primary successor - */ - public int getPrimarySuccessorRopLabel() { - return parent.blockIndexToRopLabel(primarySuccessor); - } - - /** - * @return {@code null-ok;} the primary successor block or {@code null} - * if there is none - */ - public SsaBasicBlock getPrimarySuccessor() { - if (primarySuccessor < 0) { - return null; - } else { - return parent.getBlocks().get(primarySuccessor); - } - } - - /** - * @return successor list of rop labels - */ - public IntList getRopLabelSuccessorList() { - IntList result = new IntList(successorList.size()); - - int sz = successorList.size(); - - for (int i = 0; i < sz; i++) { - result.add(parent.blockIndexToRopLabel(successorList.get(i))); - } - return result; - } - - /** - * @return {@code non-null;} method that contains this block - */ - public SsaMethod getParent() { - return parent; - } - - /** - * Inserts a new empty GOTO block as a predecessor to this block. - * All previous predecessors will be predecessors to the new block. - * - * @return {@code non-null;} an appropriately-constructed instance - */ - public SsaBasicBlock insertNewPredecessor() { - SsaBasicBlock newPred = parent.makeNewGotoBlock(); - - // Update the new block. - newPred.predecessors = predecessors; - newPred.successors.set(index) ; - newPred.successorList.add(index); - newPred.primarySuccessor = index; - - - // Update us. - predecessors = new BitSet(parent.getBlocks().size()); - predecessors.set(newPred.index); - - // Update our (soon-to-be) old predecessors. - for (int i = newPred.predecessors.nextSetBit(0); i >= 0; - i = newPred.predecessors.nextSetBit(i + 1)) { - - SsaBasicBlock predBlock = parent.getBlocks().get(i); - - predBlock.replaceSuccessor(index, newPred.index); - } - - return newPred; - } - - /** - * Constructs and inserts a new empty GOTO block {@code Z} between - * this block ({@code A}) and a current successor block - * ({@code B}). The new block will replace B as A's successor and - * A as B's predecessor. A and B will no longer be directly connected. - * If B is listed as a successor multiple times, all references - * are replaced. - * - * @param other current successor (B) - * @return {@code non-null;} an appropriately-constructed instance - */ - public SsaBasicBlock insertNewSuccessor(SsaBasicBlock other) { - SsaBasicBlock newSucc = parent.makeNewGotoBlock(); - - if (!successors.get(other.index)) { - throw new RuntimeException("Block " + other.getRopLabelString() - + " not successor of " + getRopLabelString()); - } - - // Update the new block. - newSucc.predecessors.set(this.index); - newSucc.successors.set(other.index) ; - newSucc.successorList.add(other.index); - newSucc.primarySuccessor = other.index; - - // Update us. - for (int i = successorList.size() - 1 ; i >= 0; i--) { - if (successorList.get(i) == other.index) { - successorList.set(i, newSucc.index); - } - } - - if (primarySuccessor == other.index) { - primarySuccessor = newSucc.index; - } - successors.clear(other.index); - successors.set(newSucc.index); - - // Update "other". - other.predecessors.set(newSucc.index); - other.predecessors.set(index, successors.get(other.index)); - - return newSucc; - } - - /** - * Replaces an old successor with a new successor. This will throw - * RuntimeException if {@code oldIndex} was not a successor. - * - * @param oldIndex index of old successor block - * @param newIndex index of new successor block - */ - public void replaceSuccessor(int oldIndex, int newIndex) { - if (oldIndex == newIndex) { - return; - } - - // Update us. - successors.set(newIndex); - - if (primarySuccessor == oldIndex) { - primarySuccessor = newIndex; - } - - for (int i = successorList.size() - 1 ; i >= 0; i--) { - if (successorList.get(i) == oldIndex) { - successorList.set(i, newIndex); - } - } - - successors.clear(oldIndex); - - // Update new successor. - parent.getBlocks().get(newIndex).predecessors.set(index); - - // Update old successor. - parent.getBlocks().get(oldIndex).predecessors.clear(index); - } - - /** - * Removes a successor from this block's successor list. - * - * @param oldIndex index of successor block to remove - */ - public void removeSuccessor(int oldIndex) { - int removeIndex = 0; - - for (int i = successorList.size() - 1; i >= 0; i--) { - if (successorList.get(i) == oldIndex) { - removeIndex = i; - } else { - primarySuccessor = successorList.get(i); - } - } - - successorList.removeIndex(removeIndex); - successors.clear(oldIndex); - parent.getBlocks().get(oldIndex).predecessors.clear(index); - } - - /** - * Attaches block to an exit block if necessary. If this block - * is not an exit predecessor or is the exit block, this block does - * nothing. For use by {@link com.android.jack.dx.ssa.SsaMethod#makeExitBlock} - * - * @param exitBlock {@code non-null;} exit block - */ - public void exitBlockFixup(SsaBasicBlock exitBlock) { - if (this == exitBlock) { - return; - } - - if (successorList.size() == 0) { - /* - * This is an exit predecessor. - * Set the successor to the exit block - */ - successors.set(exitBlock.index); - successorList.add(exitBlock.index); - primarySuccessor = exitBlock.index; - exitBlock.predecessors.set(this.index); - } - } - - /** - * Adds a move instruction to the end of this basic block, just - * before the last instruction. If the result of the final instruction - * is the source in question, then the move is placed at the beginning of - * the primary successor block. This is for unversioned registers. - * - * @param result move destination - * @param source move source - */ - public void addMoveToEnd(RegisterSpec result, RegisterSpec source) { - - if (result.getReg() == source.getReg()) { - // Sometimes we end up with no-op moves. Ignore them here. - return; - } + break; + } + } + } + + /** + * Deletes all phi insns. Do this after adding appropriate move insns. + */ + public void removeAllPhiInsns() { + /* + * Presently we assume PhiInsn's are in a continuous + * block at the top of the list. + */ + +insns.subList(0, getCountPhiInsns()).clear(); + } + + /** + * Gets the number of phi insns at the top of this basic block. + * + * @return count of phi insns + */ + private int getCountPhiInsns() { + int countPhiInsns; + + int sz = insns.size(); + for (countPhiInsns = 0; countPhiInsns < sz; countPhiInsns++) { + SsaInsn insn = insns.get(countPhiInsns); + if (!(insn instanceof PhiInsn)) { + break; + } + } + + return countPhiInsns; + } + + /** + * @return {@code non-null;} the (mutable) instruction list for this block, + * with phi insns at the beginning + */ + public ArrayList<SsaInsn> getInsns() { + return insns; + } + + /** + * @return {@code non-null;} the (mutable) list of phi insns for this block + */ + public List<SsaInsn> getPhiInsns() { + return insns.subList(0, getCountPhiInsns()); + } + + /** + * @return the block index of this block + */ + public int getIndex() { + return index; + } + + /** + * @return the label of this block in rop form + */ + public int getRopLabel() { + return ropLabel; + } + + /** + * @return the label of this block in rop form as a hex string + */ + public String getRopLabelString() { + return Hex.u2(ropLabel); + } + + /** + * @return {@code non-null;} predecessors set, indexed by block index + */ + public BitSet getPredecessors() { + return predecessors; + } + + /** + * @return {@code non-null;} successors set, indexed by block index + */ + public BitSet getSuccessors() { + return successors; + } + + /** + * @return {@code non-null;} ordered successor list, containing block + * indicies + */ + public IntList getSuccessorList() { + return successorList; + } + + /** + * @return {@code >= -1;} block index of primary successor or + * {@code -1} if no primary successor + */ + public int getPrimarySuccessorIndex() { + return primarySuccessor; + } + + /** + * @return rop label of primary successor + */ + public int getPrimarySuccessorRopLabel() { + return parent.blockIndexToRopLabel(primarySuccessor); + } + + /** + * @return {@code null-ok;} the primary successor block or {@code null} + * if there is none + */ + public SsaBasicBlock getPrimarySuccessor() { + if (primarySuccessor < 0) { + return null; + } else { + return parent.getBlocks().get(primarySuccessor); + } + } + + /** + * @return successor list of rop labels + */ + public IntList getRopLabelSuccessorList() { + IntList result = new IntList(successorList.size()); + + int sz = successorList.size(); + + for (int i = 0; i < sz; i++) { + result.add(parent.blockIndexToRopLabel(successorList.get(i))); + } + return result; + } + + /** + * @return {@code non-null;} method that contains this block + */ + public SsaMethod getParent() { + return parent; + } + + /** + * Inserts a new empty GOTO block as a predecessor to this block. + * All previous predecessors will be predecessors to the new block. + * + * @return {@code non-null;} an appropriately-constructed instance + */ + public SsaBasicBlock insertNewPredecessor() { + SsaBasicBlock newPred = parent.makeNewGotoBlock(); + + // Update the new block. + newPred.predecessors = predecessors; + newPred.successors.set(index); + newPred.successorList.add(index); + newPred.primarySuccessor = index; + + + // Update us. + predecessors = new BitSet(parent.getBlocks().size()); + predecessors.set(newPred.index); + + // Update our (soon-to-be) old predecessors. + for (int i = newPred.predecessors.nextSetBit(0); i >= 0; + i = newPred.predecessors.nextSetBit(i + 1)) { + + SsaBasicBlock predBlock = parent.getBlocks().get(i); + + predBlock.replaceSuccessor(index, newPred.index); + } + + return newPred; + } + + /** + * Constructs and inserts a new empty GOTO block {@code Z} between + * this block ({@code A}) and a current successor block + * ({@code B}). The new block will replace B as A's successor and + * A as B's predecessor. A and B will no longer be directly connected. + * If B is listed as a successor multiple times, all references + * are replaced. + * + * @param other current successor (B) + * @return {@code non-null;} an appropriately-constructed instance + */ + public SsaBasicBlock insertNewSuccessor(SsaBasicBlock other) { + SsaBasicBlock newSucc = parent.makeNewGotoBlock(); + + if (!successors.get(other.index)) { + throw new RuntimeException( + "Block " + other.getRopLabelString() + " not successor of " + getRopLabelString()); + } + + // Update the new block. + newSucc.predecessors.set(this.index); + newSucc.successors.set(other.index); + newSucc.successorList.add(other.index); + newSucc.primarySuccessor = other.index; + + // Update us. + for (int i = successorList.size() - 1; i >= 0; i--) { + if (successorList.get(i) == other.index) { + successorList.set(i, newSucc.index); + } + } + + if (primarySuccessor == other.index) { + primarySuccessor = newSucc.index; + } + successors.clear(other.index); + successors.set(newSucc.index); + + // Update "other". + other.predecessors.set(newSucc.index); + other.predecessors.set(index, successors.get(other.index)); + + return newSucc; + } + + /** + * Replaces an old successor with a new successor. This will throw + * RuntimeException if {@code oldIndex} was not a successor. + * + * @param oldIndex index of old successor block + * @param newIndex index of new successor block + */ + public void replaceSuccessor(int oldIndex, int newIndex) { + if (oldIndex == newIndex) { + return; + } + + // Update us. + successors.set(newIndex); + + if (primarySuccessor == oldIndex) { + primarySuccessor = newIndex; + } + + for (int i = successorList.size() - 1; i >= 0; i--) { + if (successorList.get(i) == oldIndex) { + successorList.set(i, newIndex); + } + } + + successors.clear(oldIndex); + + // Update new successor. + parent.getBlocks().get(newIndex).predecessors.set(index); + + // Update old successor. + parent.getBlocks().get(oldIndex).predecessors.clear(index); + } + + /** + * Removes a successor from this block's successor list. + * + * @param oldIndex index of successor block to remove + */ + public void removeSuccessor(int oldIndex) { + int removeIndex = 0; + + for (int i = successorList.size() - 1; i >= 0; i--) { + if (successorList.get(i) == oldIndex) { + removeIndex = i; + } else { + primarySuccessor = successorList.get(i); + } + } + + successorList.removeIndex(removeIndex); + successors.clear(oldIndex); + parent.getBlocks().get(oldIndex).predecessors.clear(index); + } + + /** + * Attaches block to an exit block if necessary. If this block + * is not an exit predecessor or is the exit block, this block does + * nothing. For use by {@link com.android.jack.dx.ssa.SsaMethod#makeExitBlock} + * + * @param exitBlock {@code non-null;} exit block + */ + public void exitBlockFixup(SsaBasicBlock exitBlock) { + if (this == exitBlock) { + return; + } + + if (successorList.size() == 0) { + /* + * This is an exit predecessor. + * Set the successor to the exit block + */ + successors.set(exitBlock.index); + successorList.add(exitBlock.index); + primarySuccessor = exitBlock.index; + exitBlock.predecessors.set(this.index); + } + } + + /** + * Adds a move instruction to the end of this basic block, just + * before the last instruction. If the result of the final instruction + * is the source in question, then the move is placed at the beginning of + * the primary successor block. This is for unversioned registers. + * + * @param result move destination + * @param source move source + */ + public void addMoveToEnd(RegisterSpec result, RegisterSpec source) { + + if (result.getReg() == source.getReg()) { + // Sometimes we end up with no-op moves. Ignore them here. + return; + } + + /* + * The last Insn has to be a normal SSA insn: a phi can't branch + * or return or cause an exception, etc. + */ + NormalSsaInsn lastInsn; + lastInsn = (NormalSsaInsn) insns.get(insns.size() - 1); + + if (lastInsn.getResult() != null || lastInsn.getSources().size() > 0) { + /* + * The final insn in this block has a source or result + * register, and the moves we may need to place and + * schedule may interfere. We need to insert this + * instruction at the beginning of the primary successor + * block instead. We know this is safe, because when we + * edge-split earlier, we ensured that each successor has + * only us as a predecessor. + */ + +for (int i = successors.nextSetBit(0); i >= 0; i = successors.nextSetBit(i + 1)) { + + SsaBasicBlock succ; + + succ = parent.getBlocks().get(i); + succ.addMoveToBeginning(result, source); + } + } else { + /* + * We can safely add a move to the end of the block just + * before the last instruction, because the final insn does + * not assign to anything. + */ + RegisterSpecList sources = RegisterSpecList.make(source); + NormalSsaInsn toAdd = new NormalSsaInsn( + new PlainInsn(Rops.opMove(result.getType()), SourcePosition.NO_INFO, result, sources), + this); + + insns.add(insns.size() - 1, toAdd); + + movesFromPhisAtEnd++; + } + } + + /** + * Adds a move instruction after the phi insn block. + * + * @param result move destination + * @param source move source + */ + public void addMoveToBeginning(RegisterSpec result, RegisterSpec source) { + if (result.getReg() == source.getReg()) { + // Sometimes we end up with no-op moves. Ignore them here. + return; + } + + RegisterSpecList sources = RegisterSpecList.make(source); + NormalSsaInsn toAdd = new NormalSsaInsn( + new PlainInsn(Rops.opMove(result.getType()), SourcePosition.NO_INFO, result, sources), + this); + + insns.add(getCountPhiInsns(), toAdd); + movesFromPhisAtBeginning++; + } + + /** + * Sets the register as used in a bitset, taking into account its + * category/width. + * + * @param regsUsed set, indexed by register number + * @param rs register to mark as used + */ + private static void setRegsUsed(BitSet regsUsed, RegisterSpec rs) { + regsUsed.set(rs.getReg()); + if (rs.getCategory() > 1) { + regsUsed.set(rs.getReg() + 1); + } + } + + /** + * Checks to see if the register is used in a bitset, taking + * into account its category/width. + * + * @param regsUsed set, indexed by register number + * @param rs register to mark as used + * @return true if register is fully or partially (for the case of wide + * registers) used. + */ + private static boolean checkRegUsed(BitSet regsUsed, RegisterSpec rs) { + int reg = rs.getReg(); + int category = rs.getCategory(); + + return regsUsed.get(reg) || (category == 2 ? regsUsed.get(reg + 1) : false); + } + + /** + * Ensures that all move operations in this block occur such that + * reads of any register happen before writes to that register. + * NOTE: caller is expected to returnSpareRegisters()! + * + * TODO(dx team): See Briggs, et al "Practical Improvements to the Construction and + * Destruction of Static Single Assignment Form" section 5. a) This can + * be done in three passes. + * + * @param toSchedule List of instructions. Must consist only of moves. + */ + private void scheduleUseBeforeAssigned(List<SsaInsn> toSchedule) { + BitSet regsUsedAsSources = new BitSet(parent.getRegCount()); + + // TODO(dx team): Get rid of this. + BitSet regsUsedAsResults = new BitSet(parent.getRegCount()); + + int sz = toSchedule.size(); + + int insertPlace = 0; + + while (insertPlace < sz) { + int oldInsertPlace = insertPlace; + + // Record all registers used as sources in this block. + for (int i = insertPlace; i < sz; i++) { + setRegsUsed(regsUsedAsSources, toSchedule.get(i).getSources().get(0)); + + setRegsUsed(regsUsedAsResults, toSchedule.get(i).getResult()); + } + + /* + * If there are no circular dependencies, then there exists + * n instructions where n > 1 whose result is not used as a source. + */ + for (int i = insertPlace; i < sz; i++) { + SsaInsn insn = toSchedule.get(i); /* - * The last Insn has to be a normal SSA insn: a phi can't branch - * or return or cause an exception, etc. + * Move these n registers to the front, since they overwrite + * nothing. */ - NormalSsaInsn lastInsn; - lastInsn = (NormalSsaInsn)insns.get(insns.size()-1); + if (!checkRegUsed(regsUsedAsSources, insn.getResult())) { + Collections.swap(toSchedule, i, insertPlace++); + } + } - if (lastInsn.getResult() != null || lastInsn.getSources().size() > 0) { - /* - * The final insn in this block has a source or result - * register, and the moves we may need to place and - * schedule may interfere. We need to insert this - * instruction at the beginning of the primary successor - * block instead. We know this is safe, because when we - * edge-split earlier, we ensured that each successor has - * only us as a predecessor. - */ + /* + * If we've made no progress in this iteration, there's a + * circular dependency. Split it using the temp reg. + */ + if (oldInsertPlace == insertPlace) { - for (int i = successors.nextSetBit(0) - ; i >= 0 - ; i = successors.nextSetBit(i + 1)) { + SsaInsn insnToSplit = null; - SsaBasicBlock succ; + // Find an insn whose result is used as a source. + for (int i = insertPlace; i < sz; i++) { + SsaInsn insn = toSchedule.get(i); + if (checkRegUsed(regsUsedAsSources, insn.getResult()) + && checkRegUsed(regsUsedAsResults, insn.getSources().get(0))) { - succ = parent.getBlocks().get(i); - succ.addMoveToBeginning(result, source); - } - } else { + insnToSplit = insn; /* - * We can safely add a move to the end of the block just - * before the last instruction, because the final insn does - * not assign to anything. + * We're going to split this insn; move it to the + * front. */ - RegisterSpecList sources = RegisterSpecList.make(source); - NormalSsaInsn toAdd = new NormalSsaInsn( - new PlainInsn(Rops.opMove(result.getType()), - SourcePosition.NO_INFO, result, sources), this); - - insns.add(insns.size() - 1, toAdd); - - movesFromPhisAtEnd++; + Collections.swap(toSchedule, insertPlace, i); + break; + } } - } - /** - * Adds a move instruction after the phi insn block. - * - * @param result move destination - * @param source move source - */ - public void addMoveToBeginning (RegisterSpec result, RegisterSpec source) { - if (result.getReg() == source.getReg()) { - // Sometimes we end up with no-op moves. Ignore them here. - return; - } - - RegisterSpecList sources = RegisterSpecList.make(source); - NormalSsaInsn toAdd = new NormalSsaInsn( - new PlainInsn(Rops.opMove(result.getType()), - SourcePosition.NO_INFO, result, sources), this); - - insns.add(getCountPhiInsns(), toAdd); - movesFromPhisAtBeginning++; - } - - /** - * Sets the register as used in a bitset, taking into account its - * category/width. - * - * @param regsUsed set, indexed by register number - * @param rs register to mark as used - */ - private static void setRegsUsed (BitSet regsUsed, RegisterSpec rs) { - regsUsed.set(rs.getReg()); - if (rs.getCategory() > 1) { - regsUsed.set(rs.getReg() + 1); - } - } - - /** - * Checks to see if the register is used in a bitset, taking - * into account its category/width. - * - * @param regsUsed set, indexed by register number - * @param rs register to mark as used - * @return true if register is fully or partially (for the case of wide - * registers) used. - */ - private static boolean checkRegUsed (BitSet regsUsed, RegisterSpec rs) { - int reg = rs.getReg(); - int category = rs.getCategory(); - - return regsUsed.get(reg) - || (category == 2 ? regsUsed.get(reg + 1) : false); - } - - /** - * Ensures that all move operations in this block occur such that - * reads of any register happen before writes to that register. - * NOTE: caller is expected to returnSpareRegisters()! - * - * TODO: See Briggs, et al "Practical Improvements to the Construction and - * Destruction of Static Single Assignment Form" section 5. a) This can - * be done in three passes. - * - * @param toSchedule List of instructions. Must consist only of moves. - */ - private void scheduleUseBeforeAssigned(List<SsaInsn> toSchedule) { - BitSet regsUsedAsSources = new BitSet(parent.getRegCount()); - - // TODO: Get rid of this. - BitSet regsUsedAsResults = new BitSet(parent.getRegCount()); - - int sz = toSchedule.size(); - - int insertPlace = 0; - - while (insertPlace < sz) { - int oldInsertPlace = insertPlace; - - // Record all registers used as sources in this block. - for (int i = insertPlace; i < sz; i++) { - setRegsUsed(regsUsedAsSources, - toSchedule.get(i).getSources().get(0)); - - setRegsUsed(regsUsedAsResults, - toSchedule.get(i).getResult()); - } - - /* - * If there are no circular dependencies, then there exists - * n instructions where n > 1 whose result is not used as a source. - */ - for (int i = insertPlace; i <sz; i++) { - SsaInsn insn = toSchedule.get(i); - - /* - * Move these n registers to the front, since they overwrite - * nothing. - */ - if (!checkRegUsed(regsUsedAsSources, insn.getResult())) { - Collections.swap(toSchedule, i, insertPlace++); - } + // At least one insn will be set above. + + RegisterSpec result = insnToSplit.getResult(); + RegisterSpec tempSpec = result.withReg(parent.borrowSpareRegister(result.getCategory())); + + NormalSsaInsn toAdd = new NormalSsaInsn(new PlainInsn(Rops.opMove(result.getType()), + SourcePosition.NO_INFO, tempSpec, insnToSplit.getSources()), this); + + toSchedule.add(insertPlace++, toAdd); + + RegisterSpecList newSources = RegisterSpecList.make(tempSpec); + + NormalSsaInsn toReplace = new NormalSsaInsn(new PlainInsn(Rops.opMove(result.getType()), + SourcePosition.NO_INFO, result, newSources), this); + + toSchedule.set(insertPlace, toReplace); + + // The size changed. + sz = toSchedule.size(); + } + + regsUsedAsSources.clear(); + regsUsedAsResults.clear(); + } + } + + /** + * Adds {@code regV} to the live-out list for this block. This is called + * by the liveness analyzer. + * + * @param regV register that is live-out for this block. + */ + public void addLiveOut(int regV) { + if (liveOut == null) { + liveOut = SetFactory.makeLivenessSet(parent.getRegCount()); + } + + liveOut.add(regV); + } + + /** + * Adds {@code regV} to the live-in list for this block. This is + * called by the liveness analyzer. + * + * @param regV register that is live-in for this block. + */ + public void addLiveIn(int regV) { + if (liveIn == null) { + liveIn = SetFactory.makeLivenessSet(parent.getRegCount()); + } + + liveIn.add(regV); + } + + /** + * Returns the set of live-in registers. Valid after register + * interference graph has been generated, otherwise empty. + * + * @return {@code non-null;} live-in register set. + */ + public IntSet getLiveInRegs() { + if (liveIn == null) { + liveIn = SetFactory.makeLivenessSet(parent.getRegCount()); + } + return liveIn; + } + + /** + * Returns the set of live-out registers. Valid after register + * interference graph has been generated, otherwise empty. + * + * @return {@code non-null;} live-out register set + */ + public IntSet getLiveOutRegs() { + if (liveOut == null) { + liveOut = SetFactory.makeLivenessSet(parent.getRegCount()); + } + return liveOut; + } + + /** + * @return true if this is the one-and-only exit block for this method + */ + public boolean isExitBlock() { + return index == parent.getExitBlockIndex(); + } + + /** + * Returns true if this block was last calculated to be reachable. + * Recalculates reachability if value has never been computed. + * + * @return {@code true} if reachable + */ + public boolean isReachable() { + if (reachable == -1) { + parent.computeReachability(); + } + return (reachable == 1); + } + + /** + * Sets reachability of block to specified value + * + * @param reach new value of reachability for block + */ + public void setReachable(int reach) { + reachable = reach; + } + + /** + * Sorts move instructions added via {@code addMoveToEnd} during + * phi removal so that results don't overwrite sources that are used. + * For use after all phis have been removed and all calls to + * addMoveToEnd() have been made.<p> + * + * This is necessary because copy-propogation may have left us in a state + * where the same basic block has the same register as a phi operand + * and a result. In this case, the register in the phi operand always + * refers value before any other phis have executed. + */ + public void scheduleMovesFromPhis() { + if (movesFromPhisAtBeginning > 1) { + List<SsaInsn> toSchedule; + + toSchedule = insns.subList(0, movesFromPhisAtBeginning); + + scheduleUseBeforeAssigned(toSchedule); + + SsaInsn firstNonPhiMoveInsn = insns.get(movesFromPhisAtBeginning); + + /* + * TODO(dx team): It's actually possible that this case never happens, + * because a move-exception block, having only one predecessor + * in SSA form, perhaps is never on a dominance frontier. + */ + if (firstNonPhiMoveInsn.isMoveException()) { + if (!enablePhisBeforeMoveException) { + /* + * We've yet to observe this case, and if it can + * occur the code written to handle it probably + * does not work. + */ + throw new RuntimeException("Unexpected: moves from " + "phis before move-exception"); + } else { + /* + * A move-exception insn must be placed first in this block + * We need to move it there, and deal with possible + * interference. + */ + boolean moveExceptionInterferes = false; + + int moveExceptionResult = firstNonPhiMoveInsn.getResult().getReg(); + + /* + * Does the move-exception result reg interfere with the + * phi moves? + */ + for (SsaInsn insn : toSchedule) { + if (insn.isResultReg(moveExceptionResult) || insn.isRegASource(moveExceptionResult)) { + moveExceptionInterferes = true; + break; } + } + if (!moveExceptionInterferes) { + // This is the easy case. + insns.remove(movesFromPhisAtBeginning); + insns.add(0, firstNonPhiMoveInsn); + } else { /* - * If we've made no progress in this iteration, there's a - * circular dependency. Split it using the temp reg. + * We need to move the result to a spare reg + * and move it back. */ - if (oldInsertPlace == insertPlace) { + RegisterSpec originalResultSpec = firstNonPhiMoveInsn.getResult(); + int spareRegister = parent.borrowSpareRegister(originalResultSpec.getCategory()); - SsaInsn insnToSplit = null; + // We now move it to a spare register. + firstNonPhiMoveInsn.changeResultReg(spareRegister); + RegisterSpec tempSpec = firstNonPhiMoveInsn.getResult(); - // Find an insn whose result is used as a source. - for (int i = insertPlace; i < sz; i++) { - SsaInsn insn = toSchedule.get(i); - if (checkRegUsed(regsUsedAsSources, insn.getResult()) - && checkRegUsed(regsUsedAsResults, - insn.getSources().get(0))) { + insns.add(0, firstNonPhiMoveInsn); - insnToSplit = insn; - /* - * We're going to split this insn; move it to the - * front. - */ - Collections.swap(toSchedule, insertPlace, i); - break; - } - } + // And here we move it back. - // At least one insn will be set above. + NormalSsaInsn toAdd = new NormalSsaInsn(new PlainInsn(Rops.opMove(tempSpec.getType()), + SourcePosition.NO_INFO, originalResultSpec, RegisterSpecList.make(tempSpec)), this); - RegisterSpec result = insnToSplit.getResult(); - RegisterSpec tempSpec = result.withReg( - parent.borrowSpareRegister(result.getCategory())); - NormalSsaInsn toAdd = new NormalSsaInsn( - new PlainInsn(Rops.opMove(result.getType()), - SourcePosition.NO_INFO, - tempSpec, - insnToSplit.getSources()), this); - - toSchedule.add(insertPlace++, toAdd); - - RegisterSpecList newSources = RegisterSpecList.make(tempSpec); - - NormalSsaInsn toReplace = new NormalSsaInsn( - new PlainInsn(Rops.opMove(result.getType()), - SourcePosition.NO_INFO, - result, - newSources), this); - - toSchedule.set(insertPlace, toReplace); - - // The size changed. - sz = toSchedule.size(); - } - - regsUsedAsSources.clear(); - regsUsedAsResults.clear(); + /* + * Place it immediately after the phi-moves, + * overwriting the move-exception that was there. + */ + insns.set(movesFromPhisAtBeginning + 1, toAdd); + } } + } } - /** - * Adds {@code regV} to the live-out list for this block. This is called - * by the liveness analyzer. - * - * @param regV register that is live-out for this block. - */ - public void addLiveOut (int regV) { - if (liveOut == null) { - liveOut = SetFactory.makeLivenessSet(parent.getRegCount()); - } - - liveOut.add(regV); + if (movesFromPhisAtEnd > 1) { + scheduleUseBeforeAssigned( + insns.subList(insns.size() - movesFromPhisAtEnd - 1, insns.size() - 1)); } - /** - * Adds {@code regV} to the live-in list for this block. This is - * called by the liveness analyzer. - * - * @param regV register that is live-in for this block. - */ - public void addLiveIn (int regV) { - if (liveIn == null) { - liveIn = SetFactory.makeLivenessSet(parent.getRegCount()); - } + // Return registers borrowed here and in scheduleUseBeforeAssigned(). + parent.returnSpareRegisters(); - liveIn.add(regV); - } + } - /** - * Returns the set of live-in registers. Valid after register - * interference graph has been generated, otherwise empty. - * - * @return {@code non-null;} live-in register set. - */ - public IntSet getLiveInRegs() { - if (liveIn == null) { - liveIn = SetFactory.makeLivenessSet(parent.getRegCount()); - } - return liveIn; - } - - /** - * Returns the set of live-out registers. Valid after register - * interference graph has been generated, otherwise empty. - * - * @return {@code non-null;} live-out register set - */ - public IntSet getLiveOutRegs() { - if (liveOut == null) { - liveOut = SetFactory.makeLivenessSet(parent.getRegCount()); - } - return liveOut; + /** + * Visits all insns in this block. + * + * @param visitor {@code non-null;} callback interface + */ + public void forEachInsn(SsaInsn.Visitor visitor) { + // This gets called a LOT, and not using an iterator + // saves a lot of allocations and reduces memory usage + int len = insns.size(); + for (int i = 0; i < len; i++) { + insns.get(i).accept(visitor); } + } - /** - * @return true if this is the one-and-only exit block for this method - */ - public boolean isExitBlock() { - return index == parent.getExitBlockIndex(); - } + /** {@inheritDoc} */ + @Override + public String toString() { + return "{" + index + ":" + Hex.u2(ropLabel) + '}'; + } + /** + * Visitor interface for basic blocks. + */ + public interface Visitor { /** - * Returns true if this block was last calculated to be reachable. - * Recalculates reachability if value has never been computed. + * Indicates a block has been visited by an iterator method. * - * @return {@code true} if reachable + * @param v {@code non-null;} block visited + * @param parent {@code null-ok;} parent node if applicable */ - public boolean isReachable() { - if (reachable == -1) { - parent.computeReachability(); - } - return (reachable == 1); - } - - /** - * Sets reachability of block to specified value - * - * @param reach new value of reachability for block - */ - public void setReachable(int reach) { - reachable = reach; - } - - /** - * Sorts move instructions added via {@code addMoveToEnd} during - * phi removal so that results don't overwrite sources that are used. - * For use after all phis have been removed and all calls to - * addMoveToEnd() have been made.<p> - * - * This is necessary because copy-propogation may have left us in a state - * where the same basic block has the same register as a phi operand - * and a result. In this case, the register in the phi operand always - * refers value before any other phis have executed. - */ - public void scheduleMovesFromPhis() { - if (movesFromPhisAtBeginning > 1) { - List<SsaInsn> toSchedule; - - toSchedule = insns.subList(0, movesFromPhisAtBeginning); - - scheduleUseBeforeAssigned(toSchedule); - - SsaInsn firstNonPhiMoveInsn = insns.get(movesFromPhisAtBeginning); - - /* - * TODO: It's actually possible that this case never happens, - * because a move-exception block, having only one predecessor - * in SSA form, perhaps is never on a dominance frontier. - */ - if (firstNonPhiMoveInsn.isMoveException()) { - if (true) { - /* - * We've yet to observe this case, and if it can - * occur the code written to handle it probably - * does not work. - */ - throw new RuntimeException( - "Unexpected: moves from " - +"phis before move-exception"); - } else { - /* - * A move-exception insn must be placed first in this block - * We need to move it there, and deal with possible - * interference. - */ - boolean moveExceptionInterferes = false; - - int moveExceptionResult - = firstNonPhiMoveInsn.getResult().getReg(); - - /* - * Does the move-exception result reg interfere with the - * phi moves? - */ - for (SsaInsn insn : toSchedule) { - if (insn.isResultReg(moveExceptionResult) - || insn.isRegASource(moveExceptionResult)) { - moveExceptionInterferes = true; - break; - } - } - - if (!moveExceptionInterferes) { - // This is the easy case. - insns.remove(movesFromPhisAtBeginning); - insns.add(0, firstNonPhiMoveInsn); - } else { - /* - * We need to move the result to a spare reg - * and move it back. - */ - RegisterSpec originalResultSpec - = firstNonPhiMoveInsn.getResult(); - int spareRegister = parent.borrowSpareRegister( - originalResultSpec.getCategory()); - - // We now move it to a spare register. - firstNonPhiMoveInsn.changeResultReg(spareRegister); - RegisterSpec tempSpec = - firstNonPhiMoveInsn.getResult(); - - insns.add(0, firstNonPhiMoveInsn); - - // And here we move it back. - - NormalSsaInsn toAdd = new NormalSsaInsn( - new PlainInsn( - Rops.opMove(tempSpec.getType()), - SourcePosition.NO_INFO, - originalResultSpec, - RegisterSpecList.make(tempSpec)), - this); - - - /* - * Place it immediately after the phi-moves, - * overwriting the move-exception that was there. - */ - insns.set(movesFromPhisAtBeginning + 1, toAdd); - } - } - } - } - - if (movesFromPhisAtEnd > 1) { - scheduleUseBeforeAssigned( - insns.subList(insns.size() - movesFromPhisAtEnd - 1, - insns.size() - 1)); - } - - // Return registers borrowed here and in scheduleUseBeforeAssigned(). - parent.returnSpareRegisters(); - - } - - /** - * Visits all insns in this block. - * - * @param visitor {@code non-null;} callback interface - */ - public void forEachInsn(SsaInsn.Visitor visitor) { - // This gets called a LOT, and not using an iterator - // saves a lot of allocations and reduces memory usage - int len = insns.size(); - for (int i = 0; i < len; i++) { - insns.get(i).accept(visitor); - } - } + void visitBlock(SsaBasicBlock v, SsaBasicBlock parent); + } + /** + * Label comparator. + */ + public static final class LabelComparator implements Comparator<SsaBasicBlock> { /** {@inheritDoc} */ @Override - public String toString() { - return "{" + index + ":" + Hex.u2(ropLabel) + '}'; - } - - /** - * Visitor interface for basic blocks. - */ - public interface Visitor { - /** - * Indicates a block has been visited by an iterator method. - * - * @param v {@code non-null;} block visited - * @param parent {@code null-ok;} parent node if applicable - */ - void visitBlock (SsaBasicBlock v, SsaBasicBlock parent); - } - - /** - * Label comparator. - */ - public static final class LabelComparator - implements Comparator<SsaBasicBlock> { - /** {@inheritDoc} */ - public int compare(SsaBasicBlock b1, SsaBasicBlock b2) { - int label1 = b1.ropLabel; - int label2 = b2.ropLabel; - - if (label1 < label2) { - return -1; - } else if (label1 > label2) { - return 1; - } else { - return 0; - } - } - } + public int compare(SsaBasicBlock b1, SsaBasicBlock b2) { + int label1 = b1.ropLabel; + int label2 = b2.ropLabel; + + if (label1 < label2) { + return -1; + } else if (label1 > label2) { + return 1; + } else { + return 0; + } + } + } } diff --git a/dx/src/com/android/jack/dx/ssa/SsaConverter.java b/dx/src/com/android/jack/dx/ssa/SsaConverter.java index 89dd19d..0c6862d 100644 --- a/dx/src/com/android/jack/dx/ssa/SsaConverter.java +++ b/dx/src/com/android/jack/dx/ssa/SsaConverter.java @@ -27,365 +27,354 @@ import java.util.BitSet; * Converts ROP methods to SSA Methods */ public class SsaConverter { - public static final boolean DEBUG = false; - - /** - * Returns an SSA representation, edge-split and with phi - * functions placed. - * - * @param rmeth input - * @param paramWidth the total width, in register-units, of the method's - * parameters - * @param isStatic {@code true} if this method has no {@code this} - * pointer argument - * @return output in SSA form + public static final boolean DEBUG = false; + + /** + * Returns an SSA representation, edge-split and with phi + * functions placed. + * + * @param rmeth input + * @param paramWidth the total width, in register-units, of the method's + * parameters + * @param isStatic {@code true} if this method has no {@code this} + * pointer argument + * @return output in SSA form + */ + public static SsaMethod convertToSsaMethod(RopMethod rmeth, int paramWidth, boolean isStatic) { + SsaMethod result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); + + edgeSplit(result); + + LocalVariableInfo localInfo = LocalVariableExtractor.extract(result); + + placePhiFunctions(result, localInfo, 0); + new SsaRenamer(result).run(); + + /* + * The exit block, added here, is not considered for edge splitting + * or phi placement since no actual control flows to it. */ - public static SsaMethod convertToSsaMethod(RopMethod rmeth, - int paramWidth, boolean isStatic) { - SsaMethod result - = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); - - edgeSplit(result); - - LocalVariableInfo localInfo = LocalVariableExtractor.extract(result); - - placePhiFunctions(result, localInfo, 0); - new SsaRenamer(result).run(); - - /* - * The exit block, added here, is not considered for edge splitting - * or phi placement since no actual control flows to it. - */ - result.makeExitBlock(); - - return result; - } - - /** - * Updates an SSA representation, placing phi functions and renaming all - * registers above a certain threshold number. - * - * @param ssaMeth input - * @param threshold registers below this number are unchanged + result.makeExitBlock(); + + return result; + } + + /** + * Updates an SSA representation, placing phi functions and renaming all + * registers above a certain threshold number. + * + * @param ssaMeth input + * @param threshold registers below this number are unchanged + */ + public static void updateSsaMethod(SsaMethod ssaMeth, int threshold) { + LocalVariableInfo localInfo = LocalVariableExtractor.extract(ssaMeth); + placePhiFunctions(ssaMeth, localInfo, threshold); + new SsaRenamer(ssaMeth, threshold).run(); + } + + /** + * Returns an SSA represention with only the edge-splitter run. + * + * @param rmeth method to process + * @param paramWidth width of all arguments in the method + * @param isStatic {@code true} if this method has no {@code this} + * pointer argument + * @return an SSA represention with only the edge-splitter run + */ + public static SsaMethod testEdgeSplit(RopMethod rmeth, int paramWidth, boolean isStatic) { + SsaMethod result; + + result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); + + edgeSplit(result); + return result; + } + + /** + * Returns an SSA represention with only the steps through the + * phi placement run. + * + * @param rmeth method to process + * @param paramWidth width of all arguments in the method + * @param isStatic {@code true} if this method has no {@code this} + * pointer argument + * @return an SSA represention with only the edge-splitter run + */ + public static SsaMethod testPhiPlacement(RopMethod rmeth, int paramWidth, boolean isStatic) { + SsaMethod result; + + result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); + + edgeSplit(result); + + LocalVariableInfo localInfo = LocalVariableExtractor.extract(result); + + placePhiFunctions(result, localInfo, 0); + return result; + } + + /** + * See Appel section 19.1: + * + * Converts CFG into "edge-split" form, such that each node either a + * unique successor or unique predecessor.<p> + * + * In addition, the SSA form we use enforces a further constraint, + * requiring each block with a final instruction that returns a + * value to have a primary successor that has no other + * predecessor. This ensures move statements can always be + * inserted correctly when phi statements are removed. + * + * @param result method to process + */ + private static void edgeSplit(SsaMethod result) { + edgeSplitPredecessors(result); + edgeSplitMoveExceptionsAndResults(result); + edgeSplitSuccessors(result); + } + + /** + * Inserts Z nodes as new predecessors for every node that has multiple + * successors and multiple predecessors. + * + * @param result {@code non-null;} method to process + */ + private static void edgeSplitPredecessors(SsaMethod result) { + ArrayList<SsaBasicBlock> blocks = result.getBlocks(); + + /* + * New blocks are added to the end of the block list during + * this iteration. */ - public static void updateSsaMethod(SsaMethod ssaMeth, int threshold) { - LocalVariableInfo localInfo = LocalVariableExtractor.extract(ssaMeth); - placePhiFunctions(ssaMeth, localInfo, threshold); - new SsaRenamer(ssaMeth, threshold).run(); + for (int i = blocks.size() - 1; i >= 0; i--) { + SsaBasicBlock block = blocks.get(i); + if (nodeNeedsUniquePredecessor(block)) { + block.insertNewPredecessor(); + } } - - /** - * Returns an SSA represention with only the edge-splitter run. - * - * @param rmeth method to process - * @param paramWidth width of all arguments in the method - * @param isStatic {@code true} if this method has no {@code this} - * pointer argument - * @return an SSA represention with only the edge-splitter run + } + + /** + * @param block {@code non-null;} block in question + * @return {@code true} if this node needs to have a unique + * predecessor created for it + */ + private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) { + /* + * Any block with that has both multiple successors and multiple + * predecessors needs a new predecessor node. */ - public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth, - boolean isStatic) { - SsaMethod result; - result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); +int countPredecessors = block.getPredecessors().cardinality(); + int countSuccessors = block.getSuccessors().cardinality(); + + return (countPredecessors > 1 && countSuccessors > 1); + } + + /** + * In ROP form, move-exception must occur as the first insn in a block + * immediately succeeding the insn that could thrown an exception. + * We may need room to insert move insns later, so make sure to split + * any block that starts with a move-exception such that there is a + * unique move-exception block for each predecessor. + * + * @param ssaMeth method to process + */ + private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) { + ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); + + /* + * New blocks are added to the end of the block list during + * this iteration. + */ + for (int i = blocks.size() - 1; i >= 0; i--) { + SsaBasicBlock block = blocks.get(i); + + /* + * Any block that starts with a move-exception and has more than + * one predecessor... + */ + if (!block.isExitBlock() && block.getPredecessors().cardinality() > 1 + && block.getInsns().get(0).isMoveException()) { + + // block.getPredecessors() is changed in the loop below. + BitSet preds = (BitSet) block.getPredecessors().clone(); + for (int j = preds.nextSetBit(0); j >= 0; j = preds.nextSetBit(j + 1)) { + SsaBasicBlock predecessor = blocks.get(j); + SsaBasicBlock zNode = predecessor.insertNewSuccessor(block); + + /* + * Make sure to place the move-exception as the + * first insn. + */ + zNode.getInsns().add(0, block.getInsns().get(0).clone()); + } - edgeSplit(result); - return result; + // Remove the move-exception from the original block. + block.getInsns().remove(0); + } } - - /** - * Returns an SSA represention with only the steps through the - * phi placement run. - * - * @param rmeth method to process - * @param paramWidth width of all arguments in the method - * @param isStatic {@code true} if this method has no {@code this} - * pointer argument - * @return an SSA represention with only the edge-splitter run + } + + /** + * Inserts Z nodes for every node that needs a new + * successor. + * + * @param result {@code non-null;} method to process + */ + private static void edgeSplitSuccessors(SsaMethod result) { + ArrayList<SsaBasicBlock> blocks = result.getBlocks(); + + /* + * New blocks are added to the end of the block list during + * this iteration. */ - public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth, - boolean isStatic) { - SsaMethod result; - - result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); + for (int i = blocks.size() - 1; i >= 0; i--) { + SsaBasicBlock block = blocks.get(i); - edgeSplit(result); + // Successors list is modified in loop below. + BitSet successors = (BitSet) block.getSuccessors().clone(); + for (int j = successors.nextSetBit(0); j >= 0; j = successors.nextSetBit(j + 1)) { - LocalVariableInfo localInfo = LocalVariableExtractor.extract(result); + SsaBasicBlock succ = blocks.get(j); - placePhiFunctions(result, localInfo, 0); - return result; - } - - /** - * See Appel section 19.1: - * - * Converts CFG into "edge-split" form, such that each node either a - * unique successor or unique predecessor.<p> - * - * In addition, the SSA form we use enforces a further constraint, - * requiring each block with a final instruction that returns a - * value to have a primary successor that has no other - * predecessor. This ensures move statements can always be - * inserted correctly when phi statements are removed. - * - * @param result method to process - */ - private static void edgeSplit(SsaMethod result) { - edgeSplitPredecessors(result); - edgeSplitMoveExceptionsAndResults(result); - edgeSplitSuccessors(result); - } - - /** - * Inserts Z nodes as new predecessors for every node that has multiple - * successors and multiple predecessors. - * - * @param result {@code non-null;} method to process - */ - private static void edgeSplitPredecessors(SsaMethod result) { - ArrayList<SsaBasicBlock> blocks = result.getBlocks(); - - /* - * New blocks are added to the end of the block list during - * this iteration. - */ - for (int i = blocks.size() - 1; i >= 0; i-- ) { - SsaBasicBlock block = blocks.get(i); - if (nodeNeedsUniquePredecessor(block)) { - block.insertNewPredecessor(); - } + if (needsNewSuccessor(block, succ)) { + block.insertNewSuccessor(succ); } + } + } + } + + /** + * Returns {@code true} if block and successor need a Z-node + * between them. Presently, this is {@code true} if the final + * instruction has any sources or results and the current + * successor block has more than one predecessor. + * + * @param block predecessor node + * @param succ successor node + * @return {@code true} if a Z node is needed + */ + private static boolean needsNewSuccessor(SsaBasicBlock block, SsaBasicBlock succ) { + ArrayList<SsaInsn> insns = block.getInsns(); + SsaInsn lastInsn = insns.get(insns.size() - 1); + + return ((lastInsn.getResult() != null) || (lastInsn.getSources().size() > 0)) + && succ.getPredecessors().cardinality() > 1; + } + + /** + * See Appel algorithm 19.6: + * + * Place Phi functions in appropriate locations. + * + * @param ssaMeth {@code non-null;} method to process. + * Modifications are made in-place. + * @param localInfo {@code non-null;} local variable info, used + * when placing phis + * @param threshold registers below this number are ignored + */ + private static void placePhiFunctions(SsaMethod ssaMeth, LocalVariableInfo localInfo, + int threshold) { + ArrayList<SsaBasicBlock> ssaBlocks; + int regCount; + int blockCount; + + ssaBlocks = ssaMeth.getBlocks(); + blockCount = ssaBlocks.size(); + regCount = ssaMeth.getRegCount() - threshold; + + DomFront df = new DomFront(ssaMeth); + DomFront.DomInfo[] domInfos = df.run(); + + // Bit set of registers vs block index "definition sites" + BitSet[] defsites = new BitSet[regCount]; + + // Bit set of registers vs block index "phi placement sites" + BitSet[] phisites = new BitSet[regCount]; + + for (int i = 0; i < regCount; i++) { + defsites[i] = new BitSet(blockCount); + phisites[i] = new BitSet(blockCount); } - /** - * @param block {@code non-null;} block in question - * @return {@code true} if this node needs to have a unique - * predecessor created for it + /* + * For each register, build a set of all basic blocks where + * containing an assignment to that register. */ - private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) { - /* - * Any block with that has both multiple successors and multiple - * predecessors needs a new predecessor node. - */ - - int countPredecessors = block.getPredecessors().cardinality(); - int countSuccessors = block.getSuccessors().cardinality(); + for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) { + SsaBasicBlock b = ssaBlocks.get(bi); - return (countPredecessors > 1 && countSuccessors > 1); - } + for (SsaInsn insn : b.getInsns()) { + RegisterSpec rs = insn.getResult(); - /** - * In ROP form, move-exception must occur as the first insn in a block - * immediately succeeding the insn that could thrown an exception. - * We may need room to insert move insns later, so make sure to split - * any block that starts with a move-exception such that there is a - * unique move-exception block for each predecessor. - * - * @param ssaMeth method to process - */ - private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) { - ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); - - /* - * New blocks are added to the end of the block list during - * this iteration. - */ - for (int i = blocks.size() - 1; i >= 0; i-- ) { - SsaBasicBlock block = blocks.get(i); - - /* - * Any block that starts with a move-exception and has more than - * one predecessor... - */ - if (!block.isExitBlock() - && block.getPredecessors().cardinality() > 1 - && block.getInsns().get(0).isMoveException()) { - - // block.getPredecessors() is changed in the loop below. - BitSet preds = (BitSet)block.getPredecessors().clone(); - for (int j = preds.nextSetBit(0); j >= 0; - j = preds.nextSetBit(j + 1)) { - SsaBasicBlock predecessor = blocks.get(j); - SsaBasicBlock zNode - = predecessor.insertNewSuccessor(block); - - /* - * Make sure to place the move-exception as the - * first insn. - */ - zNode.getInsns().add(0, block.getInsns().get(0).clone()); - } - - // Remove the move-exception from the original block. - block.getInsns().remove(0); - } + if (rs != null && rs.getReg() - threshold >= 0) { + defsites[rs.getReg() - threshold].set(bi); } + } } - /** - * Inserts Z nodes for every node that needs a new - * successor. - * - * @param result {@code non-null;} method to process - */ - private static void edgeSplitSuccessors(SsaMethod result) { - ArrayList<SsaBasicBlock> blocks = result.getBlocks(); - - /* - * New blocks are added to the end of the block list during - * this iteration. - */ - for (int i = blocks.size() - 1; i >= 0; i-- ) { - SsaBasicBlock block = blocks.get(i); - - // Successors list is modified in loop below. - BitSet successors = (BitSet)block.getSuccessors().clone(); - for (int j = successors.nextSetBit(0); - j >= 0; j = successors.nextSetBit(j+1)) { - - SsaBasicBlock succ = blocks.get(j); - - if (needsNewSuccessor(block, succ)) { - block.insertNewSuccessor(succ); - } - } - } - } + if (DEBUG) { + System.out.println("defsites"); - /** - * Returns {@code true} if block and successor need a Z-node - * between them. Presently, this is {@code true} if the final - * instruction has any sources or results and the current - * successor block has more than one predecessor. - * - * @param block predecessor node - * @param succ successor node - * @return {@code true} if a Z node is needed - */ - private static boolean needsNewSuccessor(SsaBasicBlock block, - SsaBasicBlock succ) { - ArrayList<SsaInsn> insns = block.getInsns(); - SsaInsn lastInsn = insns.get(insns.size() - 1); - - return ((lastInsn.getResult() != null) - || (lastInsn.getSources().size() > 0)) - && succ.getPredecessors().cardinality() > 1; + for (int i = 0; i < regCount; i++) { + StringBuilder sb = new StringBuilder(); + sb.append('v').append(i).append(": "); + sb.append(defsites[i].toString()); + System.out.println(sb); + } } - /** - * See Appel algorithm 19.6: - * - * Place Phi functions in appropriate locations. - * - * @param ssaMeth {@code non-null;} method to process. - * Modifications are made in-place. - * @param localInfo {@code non-null;} local variable info, used - * when placing phis - * @param threshold registers below this number are ignored - */ - private static void placePhiFunctions (SsaMethod ssaMeth, - LocalVariableInfo localInfo, int threshold) { - ArrayList<SsaBasicBlock> ssaBlocks; - int regCount; - int blockCount; - - ssaBlocks = ssaMeth.getBlocks(); - blockCount = ssaBlocks.size(); - regCount = ssaMeth.getRegCount() - threshold; + BitSet worklist; - DomFront df = new DomFront(ssaMeth); - DomFront.DomInfo[] domInfos = df.run(); - - // Bit set of registers vs block index "definition sites" - BitSet[] defsites = new BitSet[regCount]; + /* + * For each register, compute all locations for phi placement + * based on dominance-frontier algorithm. + */ + for (int reg = 0, s = regCount; reg < s; reg++) { + int workBlockIndex; - // Bit set of registers vs block index "phi placement sites" - BitSet[] phisites = new BitSet[regCount]; + /* Worklist set starts out with each node where reg is assigned. */ - for (int i = 0; i < regCount; i++) { - defsites[i] = new BitSet(blockCount); - phisites[i] = new BitSet(blockCount); - } +worklist = + (BitSet) (defsites[reg].clone()); - /* - * For each register, build a set of all basic blocks where - * containing an assignment to that register. - */ - for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) { - SsaBasicBlock b = ssaBlocks.get(bi); + while (0 <= (workBlockIndex = worklist.nextSetBit(0))) { + worklist.clear(workBlockIndex); + IntIterator dfIterator = domInfos[workBlockIndex].dominanceFrontiers.iterator(); - for (SsaInsn insn : b.getInsns()) { - RegisterSpec rs = insn.getResult(); + while (dfIterator.hasNext()) { + int dfBlockIndex = dfIterator.next(); - if (rs != null && rs.getReg() - threshold >= 0) { - defsites[rs.getReg() - threshold].set(bi); - } - } - } + if (!phisites[reg].get(dfBlockIndex)) { + phisites[reg].set(dfBlockIndex); - if (DEBUG) { - System.out.println("defsites"); + int tReg = reg + threshold; + RegisterSpec rs = localInfo.getStarts(dfBlockIndex).get(tReg); - for (int i = 0; i < regCount; i++) { - StringBuilder sb = new StringBuilder(); - sb.append('v').append(i).append(": "); - sb.append(defsites[i].toString()); - System.out.println(sb); + if (rs == null) { + ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg); + } else { + ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs); } - } - BitSet worklist; - - /* - * For each register, compute all locations for phi placement - * based on dominance-frontier algorithm. - */ - for (int reg = 0, s = regCount; reg < s; reg++) { - int workBlockIndex; - - /* Worklist set starts out with each node where reg is assigned. */ - - worklist = (BitSet) (defsites[reg].clone()); - - while (0 <= (workBlockIndex = worklist.nextSetBit(0))) { - worklist.clear(workBlockIndex); - IntIterator dfIterator - = domInfos[workBlockIndex].dominanceFrontiers.iterator(); - - while (dfIterator.hasNext()) { - int dfBlockIndex = dfIterator.next(); - - if (!phisites[reg].get(dfBlockIndex)) { - phisites[reg].set(dfBlockIndex); - - int tReg = reg + threshold; - RegisterSpec rs - = localInfo.getStarts(dfBlockIndex).get(tReg); - - if (rs == null) { - ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg); - } else { - ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs); - } - - if (!defsites[reg].get(dfBlockIndex)) { - worklist.set(dfBlockIndex); - } - } - } + if (!defsites[reg].get(dfBlockIndex)) { + worklist.set(dfBlockIndex); } + } } + } + } - if (DEBUG) { - System.out.println("phisites"); + if (DEBUG) { + System.out.println("phisites"); - for (int i = 0; i < regCount; i++) { - StringBuilder sb = new StringBuilder(); - sb.append('v').append(i).append(": "); - sb.append(phisites[i].toString()); - System.out.println(sb); - } - } + for (int i = 0; i < regCount; i++) { + StringBuilder sb = new StringBuilder(); + sb.append('v').append(i).append(": "); + sb.append(phisites[i].toString()); + System.out.println(sb); + } } + } } diff --git a/dx/src/com/android/jack/dx/ssa/SsaInsn.java b/dx/src/com/android/jack/dx/ssa/SsaInsn.java index bf76db2..403e56d 100644 --- a/dx/src/com/android/jack/dx/ssa/SsaInsn.java +++ b/dx/src/com/android/jack/dx/ssa/SsaInsn.java @@ -16,274 +16,276 @@ package com.android.jack.dx.ssa; -import com.android.jack.dx.rop.code.*; +import com.android.jack.dx.rop.code.Insn; +import com.android.jack.dx.rop.code.LocalItem; +import com.android.jack.dx.rop.code.RegisterSpec; +import com.android.jack.dx.rop.code.RegisterSpecList; +import com.android.jack.dx.rop.code.Rop; import com.android.jack.dx.util.ToHuman; /** * An instruction in SSA form */ public abstract class SsaInsn implements ToHuman, Cloneable { - /** {@code non-null;} the block that contains this instance */ - private final SsaBasicBlock block; - - /** {@code null-ok;} result register */ - private RegisterSpec result; - - /** - * Constructs an instance. - * - * @param result {@code null-ok;} initial result register. May be changed. - * @param block {@code non-null;} block containing this insn. Can - * never change. - */ - protected SsaInsn(RegisterSpec result, SsaBasicBlock block) { - if (block == null) { - throw new NullPointerException("block == null"); - } - - this.block = block; - this.result = result; - } - - /** - * Makes a new SSA insn form a rop insn. - * - * @param insn {@code non-null;} rop insn - * @param block {@code non-null;} owning block - * @return {@code non-null;} an appropriately constructed instance - */ - public static SsaInsn makeFromRop(Insn insn, SsaBasicBlock block) { - return new NormalSsaInsn(insn, block); - } - - /** {@inheritDoc} */ - @Override - public SsaInsn clone() { - try { - return (SsaInsn)super.clone(); - } catch (CloneNotSupportedException ex) { - throw new RuntimeException ("unexpected", ex); - } - } - - /** - * Like {@link com.android.jack.dx.rop.code.Insn getResult()}. - * - * @return result register - */ - public RegisterSpec getResult() { - return result; - } - - /** - * Set the result register. - * - * @param result {@code non-null;} the new result register - */ - protected void setResult(RegisterSpec result) { - if (result == null) { - throw new NullPointerException("result == null"); - } - - this.result = result; - } - - /** - * Like {@link com.android.jack.dx.rop.code.Insn getSources()}. - * - * @return {@code non-null;} sources list - */ - abstract public RegisterSpecList getSources(); - - /** - * Gets the block to which this insn instance belongs. - * - * @return owning block - */ - public SsaBasicBlock getBlock() { - return block; - } - - /** - * Returns whether or not the specified reg is the result reg. - * - * @param reg register to test - * @return true if there is a result and it is stored in the specified - * register - */ - public boolean isResultReg(int reg) { - return result != null && result.getReg() == reg; - } - - - /** - * Changes the result register if this insn has a result. This is used - * during renaming. - * - * @param reg new result register - */ - public void changeResultReg(int reg) { - if (result != null) { - result = result.withReg(reg); - } - } - - /** - * Sets the local association for the result of this insn. This is - * sometimes updated during the SsaRenamer process. - * - * @param local {@code null-ok;} new debug/local variable info - */ - public final void setResultLocal(LocalItem local) { - LocalItem oldItem = result.getLocalItem(); - - if (local != oldItem && (local == null - || !local.equals(result.getLocalItem()))) { - result = RegisterSpec.makeLocalOptional( - result.getReg(), result.getType(), local); - } + /** {@code non-null;} the block that contains this instance */ + private final SsaBasicBlock block; + + /** {@code null-ok;} result register */ + private RegisterSpec result; + + /** + * Constructs an instance. + * + * @param result {@code null-ok;} initial result register. May be changed. + * @param block {@code non-null;} block containing this insn. Can + * never change. + */ + protected SsaInsn(RegisterSpec result, SsaBasicBlock block) { + if (block == null) { + throw new NullPointerException("block == null"); } - /** - * Map registers after register allocation. - * - * @param mapper {@code non-null;} mapping from old to new registers - */ - public final void mapRegisters(RegisterMapper mapper) { - RegisterSpec oldResult = result; - - result = mapper.map(result); - block.getParent().updateOneDefinition(this, oldResult); - mapSourceRegisters(mapper); + this.block = block; + this.result = result; + } + + /** + * Makes a new SSA insn form a rop insn. + * + * @param insn {@code non-null;} rop insn + * @param block {@code non-null;} owning block + * @return {@code non-null;} an appropriately constructed instance + */ + public static SsaInsn makeFromRop(Insn insn, SsaBasicBlock block) { + return new NormalSsaInsn(insn, block); + } + + /** {@inheritDoc} */ + @Override + public SsaInsn clone() { + try { + return (SsaInsn) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new RuntimeException("unexpected", ex); } - - /** - * Maps only source registers. - * - * @param mapper new mapping - */ - abstract public void mapSourceRegisters(RegisterMapper mapper); - - /** - * Returns the Rop opcode for this insn, or null if this is a phi insn. - * - * TODO: Move this up into NormalSsaInsn. - * - * @return {@code null-ok;} Rop opcode if there is one. - */ - abstract public Rop getOpcode(); - - /** - * Returns the original Rop insn for this insn, or null if this is - * a phi insn. - * - * TODO: Move this up into NormalSsaInsn. - * - * @return {@code null-ok;} Rop insn if there is one. - */ - abstract public Insn getOriginalRopInsn(); - - /** - * Gets the spec of a local variable assignment that occurs at this - * instruction, or null if no local variable assignment occurs. This - * may be the result register, or for {@code mark-local} insns - * it may be the source. - * - * @see com.android.jack.dx.rop.code.Insn#getLocalAssignment() - * - * @return {@code null-ok;} a local-associated register spec or null - */ - public RegisterSpec getLocalAssignment() { - if (result != null && result.getLocalItem() != null) { - return result; - } - - return null; + } + + /** + * Like {@link com.android.jack.dx.rop.code.Insn getResult()}. + * + * @return result register + */ + public RegisterSpec getResult() { + return result; + } + + /** + * Set the result register. + * + * @param result {@code non-null;} the new result register + */ + protected void setResult(RegisterSpec result) { + if (result == null) { + throw new NullPointerException("result == null"); } - /** - * Indicates whether the specified register is amongst the registers - * used as sources for this instruction. - * - * @param reg the register in question - * @return true if the reg is a source - */ - public boolean isRegASource(int reg) { - return null != getSources().specForRegister(reg); + this.result = result; + } + + /** + * Like {@link com.android.jack.dx.rop.code.Insn getSources()}. + * + * @return {@code non-null;} sources list + */ + public abstract RegisterSpecList getSources(); + + /** + * Gets the block to which this insn instance belongs. + * + * @return owning block + */ + public SsaBasicBlock getBlock() { + return block; + } + + /** + * Returns whether or not the specified reg is the result reg. + * + * @param reg register to test + * @return true if there is a result and it is stored in the specified + * register + */ + public boolean isResultReg(int reg) { + return result != null && result.getReg() == reg; + } + + + /** + * Changes the result register if this insn has a result. This is used + * during renaming. + * + * @param reg new result register + */ + public void changeResultReg(int reg) { + if (result != null) { + result = result.withReg(reg); } - - /** - * Transform back to ROP form. - * - * TODO: Move this up into NormalSsaInsn. - * - * @return {@code non-null;} a ROP representation of this instruction, with - * updated registers. - */ - public abstract Insn toRopInsn(); - - /** - * @return true if this is a PhiInsn or a normal move insn - */ - public abstract boolean isPhiOrMove(); - - /** - * Returns true if this insn is considered to have a side effect beyond - * that of assigning to the result reg. - * - * @return true if this insn is considered to have a side effect beyond - * that of assigning to the result reg. - */ - public abstract boolean hasSideEffect(); - - /** - * @return true if this is a move (but not a move-operand or - * move-exception) instruction - */ - public boolean isNormalMoveInsn() { - return false; + } + + /** + * Sets the local association for the result of this insn. This is + * sometimes updated during the SsaRenamer process. + * + * @param local {@code null-ok;} new debug/local variable info + */ + public final void setResultLocal(LocalItem local) { + LocalItem oldItem = result.getLocalItem(); + + if (local != oldItem && (local == null || !local.equals(result.getLocalItem()))) { + result = RegisterSpec.makeLocalOptional(result.getReg(), result.getType(), local); } - - /** - * @return true if this is a move-exception instruction. - * These instructions must immediately follow a preceeding invoke* - */ - public boolean isMoveException() { - return false; + } + + /** + * Map registers after register allocation. + * + * @param mapper {@code non-null;} mapping from old to new registers + */ + public final void mapRegisters(RegisterMapper mapper) { + RegisterSpec oldResult = result; + + result = mapper.map(result); + block.getParent().updateOneDefinition(this, oldResult); + mapSourceRegisters(mapper); + } + + /** + * Maps only source registers. + * + * @param mapper new mapping + */ + public abstract void mapSourceRegisters(RegisterMapper mapper); + + /** + * Returns the Rop opcode for this insn, or null if this is a phi insn. + * + * TODO(dx team): Move this up into NormalSsaInsn. + * + * @return {@code null-ok;} Rop opcode if there is one. + */ + public abstract Rop getOpcode(); + + /** + * Returns the original Rop insn for this insn, or null if this is + * a phi insn. + * + * TODO(dx team): Move this up into NormalSsaInsn. + * + * @return {@code null-ok;} Rop insn if there is one. + */ + public abstract Insn getOriginalRopInsn(); + + /** + * Gets the spec of a local variable assignment that occurs at this + * instruction, or null if no local variable assignment occurs. This + * may be the result register, or for {@code mark-local} insns + * it may be the source. + * + * @see com.android.jack.dx.rop.code.Insn#getLocalAssignment() + * + * @return {@code null-ok;} a local-associated register spec or null + */ + public RegisterSpec getLocalAssignment() { + if (result != null && result.getLocalItem() != null) { + return result; } + return null; + } + + /** + * Indicates whether the specified register is amongst the registers + * used as sources for this instruction. + * + * @param reg the register in question + * @return true if the reg is a source + */ + public boolean isRegASource(int reg) { + return null != getSources().specForRegister(reg); + } + + /** + * Transform back to ROP form. + * + * TODO(dx team): Move this up into NormalSsaInsn. + * + * @return {@code non-null;} a ROP representation of this instruction, with + * updated registers. + */ + public abstract Insn toRopInsn(); + + /** + * @return true if this is a PhiInsn or a normal move insn + */ + public abstract boolean isPhiOrMove(); + + /** + * Returns true if this insn is considered to have a side effect beyond + * that of assigning to the result reg. + * + * @return true if this insn is considered to have a side effect beyond + * that of assigning to the result reg. + */ + public abstract boolean hasSideEffect(); + + /** + * @return true if this is a move (but not a move-operand or + * move-exception) instruction + */ + public boolean isNormalMoveInsn() { + return false; + } + + /** + * @return true if this is a move-exception instruction. + * These instructions must immediately follow a preceeding invoke* + */ + public boolean isMoveException() { + return false; + } + + /** + * @return true if this instruction can throw. + */ + public abstract boolean canThrow(); + + /** + * Accepts a visitor. + * + * @param v {@code non-null} the visitor + */ + public abstract void accept(Visitor v); + + /** + * Visitor interface for this class. + */ + public static interface Visitor { /** - * @return true if this instruction can throw. + * Any non-phi move instruction + * @param insn {@code non-null;} the instruction to visit */ - abstract public boolean canThrow(); + public void visitMoveInsn(NormalSsaInsn insn); /** - * Accepts a visitor. - * - * @param v {@code non-null} the visitor + * Any phi insn + * @param insn {@code non-null;} the instruction to visit */ - public abstract void accept(Visitor v); + public void visitPhiInsn(PhiInsn insn); /** - * Visitor interface for this class. + * Any insn that isn't a move or a phi (which is also a move). + * @param insn {@code non-null;} the instruction to visit */ - public static interface Visitor { - /** - * Any non-phi move instruction - * @param insn {@code non-null;} the instruction to visit - */ - public void visitMoveInsn(NormalSsaInsn insn); - - /** - * Any phi insn - * @param insn {@code non-null;} the instruction to visit - */ - public void visitPhiInsn(PhiInsn insn); - - /** - * Any insn that isn't a move or a phi (which is also a move). - * @param insn {@code non-null;} the instruction to visit - */ - public void visitNonMoveInsn(NormalSsaInsn insn); - } + public void visitNonMoveInsn(NormalSsaInsn insn); + } } diff --git a/dx/src/com/android/jack/dx/ssa/SsaMethod.java b/dx/src/com/android/jack/dx/ssa/SsaMethod.java index f0166df..314bddb 100644 --- a/dx/src/com/android/jack/dx/ssa/SsaMethod.java +++ b/dx/src/com/android/jack/dx/ssa/SsaMethod.java @@ -39,835 +39,839 @@ import java.util.Stack; * A method in SSA form. */ public final class SsaMethod { - /** basic blocks, indexed by block index */ - private ArrayList<SsaBasicBlock> blocks; - - /** Index of first executed block in method */ - private int entryBlockIndex; - - /** - * Index of exit block, which exists only in SSA form, - * or or {@code -1} if there is none - */ - private int exitBlockIndex; - - /** total number of registers required */ - private int registerCount; - - /** first register number to use for any temporary "spares" */ - private int spareRegisterBase; - - /** current count of spare registers used */ - private int borrowedSpareRegisters; - - /** really one greater than the max label */ - private int maxLabel; - - /** the total width, in register-units, of the method's parameters */ - private final int paramWidth; - - /** true if this method has no {@code this} pointer argument */ - private final boolean isStatic; - - /** - * indexed by register: the insn where said register is defined or null - * if undefined. null until (lazily) created. - */ - private SsaInsn[] definitionList; - - /** indexed by register: the list of all insns that use a register */ - private ArrayList<SsaInsn>[] useList; - - /** A version of useList with each List unmodifiable */ - private List<SsaInsn>[] unmodifiableUseList; - - /** - * "back-convert mode". Set during back-conversion when registers - * are about to be mapped into a non-SSA namespace. When true, - * use and def lists are unavailable. - * - * TODO: Remove this mode, and place the functionality elsewhere - */ - private boolean backMode; - - /** - * @param ropMethod rop-form method to convert from - * @param paramWidth the total width, in register-units, of the - * method's parameters - * @param isStatic {@code true} if this method has no {@code this} - * pointer argument - */ - public static SsaMethod newFromRopMethod(RopMethod ropMethod, - int paramWidth, boolean isStatic) { - SsaMethod result = new SsaMethod(ropMethod, paramWidth, isStatic); - - result.convertRopToSsaBlocks(ropMethod); - - return result; - } - - /** - * Constructs an instance. - * - * @param ropMethod {@code non-null;} the original rop-form method that - * this instance is based on - * @param paramWidth the total width, in register-units, of the - * method's parameters - * @param isStatic {@code true} if this method has no {@code this} - * pointer argument - */ - private SsaMethod(RopMethod ropMethod, int paramWidth, boolean isStatic) { - this.paramWidth = paramWidth; - this.isStatic = isStatic; - this.backMode = false; - this.maxLabel = ropMethod.getBlocks().getMaxLabel(); - this.registerCount = ropMethod.getBlocks().getRegCount(); - this.spareRegisterBase = registerCount; - } - - /** - * Builds a BitSet of block indices from a basic block list and a list - * of labels taken from Rop form. - * - * @param blocks Rop blocks - * @param labelList list of rop block labels - * @return BitSet of block indices - */ - static BitSet bitSetFromLabelList(BasicBlockList blocks, - IntList labelList) { - BitSet result = new BitSet(blocks.size()); - - for (int i = 0, sz = labelList.size(); i < sz; i++) { - result.set(blocks.indexOfLabel(labelList.get(i))); - } - - return result; - } - - /** - * Builds an IntList of block indices from a basic block list and a list - * of labels taken from Rop form. - * - * @param ropBlocks Rop blocks - * @param labelList list of rop block labels - * @return IntList of block indices - */ - public static IntList indexListFromLabelList(BasicBlockList ropBlocks, - IntList labelList) { - - IntList result = new IntList(labelList.size()); - - for (int i = 0, sz = labelList.size(); i < sz; i++) { - result.add(ropBlocks.indexOfLabel(labelList.get(i))); - } - - return result; - } - - private void convertRopToSsaBlocks(RopMethod rmeth) { - BasicBlockList ropBlocks = rmeth.getBlocks(); - int sz = ropBlocks.size(); - - blocks = new ArrayList<SsaBasicBlock>(sz + 2); + /** basic blocks, indexed by block index */ + private ArrayList<SsaBasicBlock> blocks; + + /** Index of first executed block in method */ + private int entryBlockIndex; + + /** + * Index of exit block, which exists only in SSA form, + * or or {@code -1} if there is none + */ + private int exitBlockIndex; + + /** total number of registers required */ + private int registerCount; + + /** first register number to use for any temporary "spares" */ + private int spareRegisterBase; + + /** current count of spare registers used */ + private int borrowedSpareRegisters; + + /** really one greater than the max label */ + private int maxLabel; + + /** the total width, in register-units, of the method's parameters */ + private final int paramWidth; + + /** true if this method has no {@code this} pointer argument */ + private final boolean isStatic; + + /** + * indexed by register: the insn where said register is defined or null + * if undefined. null until (lazily) created. + */ + private SsaInsn[] definitionList; + + /** indexed by register: the list of all insns that use a register */ + private ArrayList<SsaInsn>[] useList; + + /** A version of useList with each List unmodifiable */ + private List<SsaInsn>[] unmodifiableUseList; + + /** + * "back-convert mode". Set during back-conversion when registers + * are about to be mapped into a non-SSA namespace. When true, + * use and def lists are unavailable. + * + * TODO(dx team): Remove this mode, and place the functionality elsewhere + */ + private boolean backMode; + + /** + * @param ropMethod rop-form method to convert from + * @param paramWidth the total width, in register-units, of the + * method's parameters + * @param isStatic {@code true} if this method has no {@code this} + * pointer argument + */ + public static SsaMethod newFromRopMethod(RopMethod ropMethod, int paramWidth, boolean isStatic) { + SsaMethod result = new SsaMethod(ropMethod, paramWidth, isStatic); + + result.convertRopToSsaBlocks(ropMethod); + + return result; + } + + /** + * Constructs an instance. + * + * @param ropMethod {@code non-null;} the original rop-form method that + * this instance is based on + * @param paramWidth the total width, in register-units, of the + * method's parameters + * @param isStatic {@code true} if this method has no {@code this} + * pointer argument + */ + private SsaMethod(RopMethod ropMethod, int paramWidth, boolean isStatic) { + this.paramWidth = paramWidth; + this.isStatic = isStatic; + this.backMode = false; + this.maxLabel = ropMethod.getBlocks().getMaxLabel(); + this.registerCount = ropMethod.getBlocks().getRegCount(); + this.spareRegisterBase = registerCount; + } + + /** + * Builds a BitSet of block indices from a basic block list and a list + * of labels taken from Rop form. + * + * @param blocks Rop blocks + * @param labelList list of rop block labels + * @return BitSet of block indices + */ + static BitSet bitSetFromLabelList(BasicBlockList blocks, IntList labelList) { + BitSet result = new BitSet(blocks.size()); + + for (int i = 0, sz = labelList.size(); i < sz; i++) { + result.set(blocks.indexOfLabel(labelList.get(i))); + } + + return result; + } + + /** + * Builds an IntList of block indices from a basic block list and a list + * of labels taken from Rop form. + * + * @param ropBlocks Rop blocks + * @param labelList list of rop block labels + * @return IntList of block indices + */ + public static IntList indexListFromLabelList(BasicBlockList ropBlocks, IntList labelList) { + + IntList result = new IntList(labelList.size()); + + for (int i = 0, sz = labelList.size(); i < sz; i++) { + result.add(ropBlocks.indexOfLabel(labelList.get(i))); + } + + return result; + } + + private void convertRopToSsaBlocks(RopMethod rmeth) { + BasicBlockList ropBlocks = rmeth.getBlocks(); + int sz = ropBlocks.size(); + + blocks = new ArrayList<SsaBasicBlock>(sz + 2); + + for (int i = 0; i < sz; i++) { + SsaBasicBlock sbb = SsaBasicBlock.newFromRop(rmeth, i, this); + blocks.add(sbb); + } + + // Add an no-op entry block. + int origEntryBlockIndex = rmeth.getBlocks().indexOfLabel(rmeth.getFirstLabel()); + + SsaBasicBlock entryBlock = blocks.get(origEntryBlockIndex).insertNewPredecessor(); + + entryBlockIndex = entryBlock.getIndex(); + exitBlockIndex = -1; // This gets made later. + } + + /** + * Creates an exit block and attaches it to the CFG if this method + * exits. Methods that never exit will not have an exit block. This + * is called after edge-splitting and phi insertion, since the edges + * going into the exit block should not be considered in those steps. + */ + /*package*/void makeExitBlock() { + if (exitBlockIndex >= 0) { + throw new RuntimeException("must be called at most once"); + } + + exitBlockIndex = blocks.size(); + SsaBasicBlock exitBlock = new SsaBasicBlock(exitBlockIndex, maxLabel++, this); + + blocks.add(exitBlock); + + for (SsaBasicBlock block : blocks) { + block.exitBlockFixup(exitBlock); + } + + if (exitBlock.getPredecessors().cardinality() == 0) { + // In cases where there is no exit... + blocks.remove(exitBlockIndex); + exitBlockIndex = -1; + maxLabel--; + } + } + + /** + * Gets a new {@code GOTO} insn. + * + * @param block block to which this GOTO will be added + * (not it's destination!) + * @return an appropriately-constructed instance. + */ + private static SsaInsn getGoto(SsaBasicBlock block) { + return new NormalSsaInsn( + new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY), block); + } + + /** + * Makes a new basic block for this method, which is empty besides + * a single {@code GOTO}. Successors and predecessors are not yet + * set. + * + * @return new block + */ + public SsaBasicBlock makeNewGotoBlock() { + int newIndex = blocks.size(); + SsaBasicBlock newBlock = new SsaBasicBlock(newIndex, maxLabel++, this); + + newBlock.getInsns().add(getGoto(newBlock)); + blocks.add(newBlock); + + return newBlock; + } + + /** + * @return block index of first execution block + */ + public int getEntryBlockIndex() { + return entryBlockIndex; + } + + /** + * @return first execution block + */ + public SsaBasicBlock getEntryBlock() { + return blocks.get(entryBlockIndex); + } + + /** + * @return block index of exit block or {@code -1} if there is none + */ + public int getExitBlockIndex() { + return exitBlockIndex; + } + + /** + * @return {@code null-ok;} block of exit block or {@code null} if + * there is none + */ + public SsaBasicBlock getExitBlock() { + return exitBlockIndex < 0 ? null : blocks.get(exitBlockIndex); + } + + /** + * @param bi block index or {@code -1} for none + * @return rop label or {code -1} if {@code bi} was {@code -1} + */ + public int blockIndexToRopLabel(int bi) { + if (bi < 0) { + return -1; + } + return blocks.get(bi).getRopLabel(); + } + + /** + * @return count of registers used in this method + */ + public int getRegCount() { + return registerCount; + } + + /** + * @return the total width, in register units, of the method's + * parameters + */ + public int getParamWidth() { + return paramWidth; + } + + /** + * Returns {@code true} if this is a static method. + * + * @return {@code true} if this is a static method + */ + public boolean isStatic() { + return isStatic; + } + + /** + * Borrows a register to use as a temp. Used in the phi removal process. + * Call returnSpareRegisters() when done. + * + * @param category width (1 or 2) of the register + * @return register number to use + */ + public int borrowSpareRegister(int category) { + int result = spareRegisterBase + borrowedSpareRegisters; + + borrowedSpareRegisters += category; + registerCount = Math.max(registerCount, result + category); + + return result; + } + + /** + * Returns all borrowed registers. + */ + public void returnSpareRegisters() { + borrowedSpareRegisters = 0; + } + + /** + * @return {@code non-null;} basic block list. Do not modify. + */ + public ArrayList<SsaBasicBlock> getBlocks() { + return blocks; + } + + /** + * Returns the count of reachable blocks in this method: blocks that have + * predecessors (or are the start block) + * + * @return {@code >= 0;} number of reachable basic blocks + */ + public int getCountReachableBlocks() { + int ret = 0; + + for (SsaBasicBlock b : blocks) { + // Blocks that have been disconnected don't count. + if (b.isReachable()) { + ret++; + } + } + + return ret; + } + + /** + * Computes reachability for all blocks in the method. First clears old + * values from all blocks, then starts with the entry block and walks down + * the control flow graph, marking all blocks it finds as reachable. + */ + public void computeReachability() { + for (SsaBasicBlock block : blocks) { + block.setReachable(0); + } + + ArrayList<SsaBasicBlock> blockList = new ArrayList<SsaBasicBlock>(); + blockList.add(this.getEntryBlock()); + + while (!blockList.isEmpty()) { + SsaBasicBlock block = blockList.remove(0); + if (block.isReachable()) { + continue; + } + + block.setReachable(1); + BitSet succs = block.getSuccessors(); + for (int i = succs.nextSetBit(0); i >= 0; i = succs.nextSetBit(i + 1)) { + blockList.add(blocks.get(i)); + } + } + } + + /** + * Remaps unversioned registers. + * + * @param mapper maps old registers to new. + */ + public void mapRegisters(RegisterMapper mapper) { + for (SsaBasicBlock block : getBlocks()) { + for (SsaInsn insn : block.getInsns()) { + insn.mapRegisters(mapper); + } + } + + registerCount = mapper.getNewRegisterCount(); + spareRegisterBase = registerCount; + } + + /** + * Returns the insn that defines the given register + * @param reg register in question + * @return insn (actual instance from code) that defined this reg or null + * if reg is not defined. + */ + public SsaInsn getDefinitionForRegister(int reg) { + if (backMode) { + throw new RuntimeException("No def list in back mode"); + } + + if (definitionList != null) { + return definitionList[reg]; + } + + definitionList = new SsaInsn[getRegCount()]; + + forEachInsn(new SsaInsn.Visitor() { + @Override + public void visitMoveInsn(NormalSsaInsn insn) { + definitionList[insn.getResult().getReg()] = insn; + } + + @Override + public void visitPhiInsn(PhiInsn phi) { + definitionList[phi.getResult().getReg()] = phi; + } + + @Override + public void visitNonMoveInsn(NormalSsaInsn insn) { + RegisterSpec result = insn.getResult(); + if (result != null) { + definitionList[insn.getResult().getReg()] = insn; + } + } + }); + + return definitionList[reg]; + } + + /** + * Builds useList and unmodifiableUseList. + */ + @SuppressWarnings("unchecked") + private void buildUseList() { + if (backMode) { + throw new RuntimeException("No use list in back mode"); + } + + useList = new ArrayList[registerCount]; + + for (int i = 0; i < registerCount; i++) { + useList[i] = new ArrayList<SsaInsn>(); + } + + forEachInsn(new SsaInsn.Visitor() { + /** {@inheritDoc} */ + @Override + public void visitMoveInsn(NormalSsaInsn insn) { + addToUses(insn); + } + + /** {@inheritDoc} */ + @Override + public void visitPhiInsn(PhiInsn phi) { + addToUses(phi); + } + + /** {@inheritDoc} */ + @Override + public void visitNonMoveInsn(NormalSsaInsn insn) { + addToUses(insn); + } + + /** + * Adds specified insn to the uses list for all of its sources. + * @param insn {@code non-null;} insn to process + */ + private void addToUses(SsaInsn insn) { + RegisterSpecList rl = insn.getSources(); + int sz = rl.size(); for (int i = 0; i < sz; i++) { - SsaBasicBlock sbb = SsaBasicBlock.newFromRop(rmeth, i, this); - blocks.add(sbb); - } - - // Add an no-op entry block. - int origEntryBlockIndex = rmeth.getBlocks() - .indexOfLabel(rmeth.getFirstLabel()); - - SsaBasicBlock entryBlock - = blocks.get(origEntryBlockIndex).insertNewPredecessor(); - - entryBlockIndex = entryBlock.getIndex(); - exitBlockIndex = -1; // This gets made later. - } - - /** - * Creates an exit block and attaches it to the CFG if this method - * exits. Methods that never exit will not have an exit block. This - * is called after edge-splitting and phi insertion, since the edges - * going into the exit block should not be considered in those steps. - */ - /*package*/ void makeExitBlock() { - if (exitBlockIndex >= 0) { - throw new RuntimeException("must be called at most once"); - } - - exitBlockIndex = blocks.size(); - SsaBasicBlock exitBlock - = new SsaBasicBlock(exitBlockIndex, maxLabel++, this); - - blocks.add(exitBlock); - - for (SsaBasicBlock block : blocks) { - block.exitBlockFixup(exitBlock); - } - - if (exitBlock.getPredecessors().cardinality() == 0) { - // In cases where there is no exit... - blocks.remove(exitBlockIndex); - exitBlockIndex = -1; - maxLabel--; - } - } - - /** - * Gets a new {@code GOTO} insn. - * - * @param block block to which this GOTO will be added - * (not it's destination!) - * @return an appropriately-constructed instance. - */ - private static SsaInsn getGoto(SsaBasicBlock block) { - return new NormalSsaInsn ( - new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO, - null, RegisterSpecList.EMPTY), block); - } - - /** - * Makes a new basic block for this method, which is empty besides - * a single {@code GOTO}. Successors and predecessors are not yet - * set. - * - * @return new block - */ - public SsaBasicBlock makeNewGotoBlock() { - int newIndex = blocks.size(); - SsaBasicBlock newBlock = new SsaBasicBlock(newIndex, maxLabel++, this); - - newBlock.getInsns().add(getGoto(newBlock)); - blocks.add(newBlock); - - return newBlock; - } - - /** - * @return block index of first execution block - */ - public int getEntryBlockIndex() { - return entryBlockIndex; - } - - /** - * @return first execution block - */ - public SsaBasicBlock getEntryBlock() { - return blocks.get(entryBlockIndex); - } - - /** - * @return block index of exit block or {@code -1} if there is none - */ - public int getExitBlockIndex() { - return exitBlockIndex; - } - - /** - * @return {@code null-ok;} block of exit block or {@code null} if - * there is none - */ - public SsaBasicBlock getExitBlock() { - return exitBlockIndex < 0 ? null : blocks.get(exitBlockIndex); - } - - /** - * @param bi block index or {@code -1} for none - * @return rop label or {code -1} if {@code bi} was {@code -1} - */ - public int blockIndexToRopLabel(int bi) { - if (bi < 0) { - return -1; - } - return blocks.get(bi).getRopLabel(); - } - - /** - * @return count of registers used in this method - */ - public int getRegCount() { - return registerCount; - } - - /** - * @return the total width, in register units, of the method's - * parameters - */ - public int getParamWidth() { - return paramWidth; - } - - /** - * Returns {@code true} if this is a static method. - * - * @return {@code true} if this is a static method - */ - public boolean isStatic() { - return isStatic; - } - - /** - * Borrows a register to use as a temp. Used in the phi removal process. - * Call returnSpareRegisters() when done. - * - * @param category width (1 or 2) of the register - * @return register number to use - */ - public int borrowSpareRegister(int category) { - int result = spareRegisterBase + borrowedSpareRegisters; + useList[rl.get(i).getReg()].add(insn); + } + } + }); + + unmodifiableUseList = new List[registerCount]; + + for (int i = 0; i < registerCount; i++) { + unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]); + } + } + + /** + * Updates the use list for a single change in source register. + * + * @param insn {@code non-null;} insn being changed + * @param oldSource {@code null-ok;} The source that was used, if + * applicable + * @param newSource {@code non-null;} the new source being used + */ + /*package*/void onSourceChanged(SsaInsn insn, RegisterSpec oldSource, RegisterSpec newSource) { + if (useList == null) { + return; + } + + if (oldSource != null) { + int reg = oldSource.getReg(); + useList[reg].remove(insn); + } + + int reg = newSource.getReg(); + if (useList.length <= reg) { + useList = null; + return; + } + useList[reg].add(insn); + } + + /** + * Updates the use list for a source list change. + * + * @param insn {@code insn non-null;} insn being changed. + * {@code insn.getSources()} must return the new source list. + * @param oldSources {@code null-ok;} list of sources that were + * previously used + */ + /*package*/void onSourcesChanged(SsaInsn insn, RegisterSpecList oldSources) { + if (useList == null) { + return; + } + + if (oldSources != null) { + removeFromUseList(insn, oldSources); + } + + RegisterSpecList sources = insn.getSources(); + int szNew = sources.size(); + + for (int i = 0; i < szNew; i++) { + int reg = sources.get(i).getReg(); + useList[reg].add(insn); + } + } + + /** + * Removes a given {@code insn} from the use lists for the given + * {@code oldSources} (rather than the sources currently + * returned by insn.getSources()). + * + * @param insn {@code non-null;} insn in question + * @param oldSources {@code null-ok;} registers whose use lists + * {@code insn} should be removed form + */ + private void removeFromUseList(SsaInsn insn, RegisterSpecList oldSources) { + if (oldSources == null) { + return; + } + + int szNew = oldSources.size(); + for (int i = 0; i < szNew; i++) { + if (!useList[oldSources.get(i).getReg()].remove(insn)) { + throw new RuntimeException("use not found"); + } + } + } + + /** + * Adds an insn to both the use and def lists. For use when adding + * a new insn to the method. + * + * @param insn {@code non-null;} insn to add + */ + /*package*/void onInsnAdded(SsaInsn insn) { + onSourcesChanged(insn, null); + updateOneDefinition(insn, null); + } + + /** + * Removes an instruction from use and def lists. For use during + * instruction removal. + * + * @param insn {@code non-null;} insn to remove + */ + /*package*/void onInsnRemoved(SsaInsn insn) { + if (useList != null) { + removeFromUseList(insn, insn.getSources()); + } + + RegisterSpec resultReg = insn.getResult(); + if (definitionList != null && resultReg != null) { + definitionList[resultReg.getReg()] = null; + } + } + + /** + * Indicates that the instruction list has changed or the SSA register + * count has increased, so that internal datastructures that rely on + * it should be rebuild. In general, the various other on* methods + * should be called in preference when changes occur if they are + * applicable. + */ + public void onInsnsChanged() { + // Definition list will need to be recomputed + definitionList = null; + + // Use list will need to be recomputed + useList = null; + unmodifiableUseList = null; + } + + /** + * Updates a single definition. + * + * @param insn {@code non-null;} insn who's result should be recorded as + * a definition + * @param oldResult {@code null-ok;} a previous result that should + * be no longer considered a definition by this insn + */ + /*package*/void updateOneDefinition(SsaInsn insn, RegisterSpec oldResult) { + if (definitionList == null) { + return; + } + + if (oldResult != null) { + int reg = oldResult.getReg(); + definitionList[reg] = null; + } + + RegisterSpec resultReg = insn.getResult(); + + if (resultReg != null) { + int reg = resultReg.getReg(); + + if (definitionList[reg] != null) { + throw new RuntimeException("Duplicate add of insn"); + } else { + definitionList[resultReg.getReg()] = insn; + } + } + } + + /** + * Returns the list of all source uses (not results) for a register. + * + * @param reg register in question + * @return unmodifiable instruction list + */ + public List<SsaInsn> getUseListForRegister(int reg) { + + if (unmodifiableUseList == null) { + buildUseList(); + } + + return unmodifiableUseList[reg]; + } + + /** + * Returns a modifiable copy of the register use list. + * + * @return modifiable copy of the use-list, indexed by register + */ + public ArrayList<SsaInsn>[] getUseListCopy() { + if (useList == null) { + buildUseList(); + } + + @SuppressWarnings("unchecked") + ArrayList<SsaInsn>[] useListCopy = new ArrayList[registerCount]; + + for (int i = 0; i < registerCount; i++) { + useListCopy[i] = new ArrayList<SsaInsn>(useList[i]); + } + + return useListCopy; + } + + /** + * Checks to see if the given SSA reg is ever associated with a local + * local variable. Each SSA reg may be associated with at most one + * local var. + * + * @param spec {@code non-null;} ssa reg + * @return true if reg is ever associated with a local + */ + public boolean isRegALocal(RegisterSpec spec) { + SsaInsn defn = getDefinitionForRegister(spec.getReg()); + + if (defn == null) { + // version 0 registers are never used as locals + return false; + } + + // Does the definition have a local associated with it? + if (defn.getLocalAssignment() != null) { + return true; + } + + // If not, is there a mark-local insn? + for (SsaInsn use : getUseListForRegister(spec.getReg())) { + Insn insn = use.getOriginalRopInsn(); + + if (insn != null && insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) { + return true; + } + } + + return false; + } + + /** + * Sets the new register count after renaming. + * + * @param newRegCount new register count + */ + /*package*/void setNewRegCount(int newRegCount) { + registerCount = newRegCount; + spareRegisterBase = registerCount; + onInsnsChanged(); + } + + /** + * Makes a new SSA register. For use after renaming has completed. + * + * @return {@code >=0;} new SSA register. + */ + public int makeNewSsaReg() { + int reg = registerCount++; + spareRegisterBase = registerCount; + onInsnsChanged(); + return reg; + } + + /** + * Visits all insns in this method. + * + * @param visitor {@code non-null;} callback interface + */ + public void forEachInsn(SsaInsn.Visitor visitor) { + for (SsaBasicBlock block : blocks) { + block.forEachInsn(visitor); + } + } + + /** + * Visits each phi insn in this method + * @param v {@code non-null;} callback. + * + */ + public void forEachPhiInsn(PhiInsn.Visitor v) { + for (SsaBasicBlock block : blocks) { + block.forEachPhiInsn(v); + } + } + + + /** + * Walks the basic block tree in depth-first order, calling the visitor + * method once for every block. This depth-first walk may be run forward + * from the method entry point or backwards from the method exit points. + * + * @param reverse true if this should walk backwards from the exit points + * @param v {@code non-null;} callback interface. {@code parent} is set + * unless this is the root node + */ + public void forEachBlockDepthFirst(boolean reverse, SsaBasicBlock.Visitor v) { + BitSet visited = new BitSet(blocks.size()); + + // We push the parent first, then the child on the stack. + Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>(); + + SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock(); + + if (rootBlock == null) { + // in the case there's no exit block + return; + } + + stack.add(null); // Start with null parent. + stack.add(rootBlock); + + while (stack.size() > 0) { + SsaBasicBlock cur = stack.pop(); + SsaBasicBlock parent = stack.pop(); + + if (!visited.get(cur.getIndex())) { + BitSet children = reverse ? cur.getPredecessors() : cur.getSuccessors(); + for (int i = children.nextSetBit(0); i >= 0; i = children.nextSetBit(i + 1)) { + stack.add(cur); + stack.add(blocks.get(i)); + } + visited.set(cur.getIndex()); + v.visitBlock(cur, parent); + } + } + } + + /** + * Visits blocks in dom-tree order, starting at the current node. + * The {@code parent} parameter of the Visitor.visitBlock callback + * is currently always set to null. + * + * @param v {@code non-null;} callback interface + */ + public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) { + BitSet visited = new BitSet(getBlocks().size()); + Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>(); + + stack.add(getEntryBlock()); + + while (stack.size() > 0) { + SsaBasicBlock cur = stack.pop(); + ArrayList<SsaBasicBlock> curDomChildren = cur.getDomChildren(); + + if (!visited.get(cur.getIndex())) { + // We walk the tree this way for historical reasons... + for (int i = curDomChildren.size() - 1; i >= 0; i--) { + SsaBasicBlock child = curDomChildren.get(i); + stack.add(child); + } + visited.set(cur.getIndex()); + v.visitBlock(cur, null); + } + } + } + + /** + * Deletes all insns in the set from this method. + * + * @param deletedInsns {@code non-null;} insns to delete + */ + public void deleteInsns(Set<SsaInsn> deletedInsns) { + for (SsaBasicBlock block : getBlocks()) { + ArrayList<SsaInsn> insns = block.getInsns(); + + for (int i = insns.size() - 1; i >= 0; i--) { + SsaInsn insn = insns.get(i); + + if (deletedInsns.contains(insn)) { + onInsnRemoved(insn); + insns.remove(i); + } + } + + // Check to see if we need to add a GOTO + + int insnsSz = insns.size(); + SsaInsn lastInsn = (insnsSz == 0) ? null : insns.get(insnsSz - 1); + + if (block != getExitBlock() && (insnsSz == 0 || lastInsn.getOriginalRopInsn() == null + || lastInsn.getOriginalRopInsn().getOpcode().getBranchingness() == Rop.BRANCH_NONE)) { + // We managed to eat a throwable insn + + Insn gotoInsn = + new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY); + insns.add(SsaInsn.makeFromRop(gotoInsn, block)); + + // Remove secondary successors from this block + BitSet succs = block.getSuccessors(); + for (int i = succs.nextSetBit(0); i >= 0; i = succs.nextSetBit(i + 1)) { + if (i != block.getPrimarySuccessorIndex()) { + block.removeSuccessor(i); + } + } + } + } + } - borrowedSpareRegisters += category; - registerCount = Math.max(registerCount, result + category); - - return result; - } - - /** - * Returns all borrowed registers. - */ - public void returnSpareRegisters() { - borrowedSpareRegisters = 0; - } - - /** - * @return {@code non-null;} basic block list. Do not modify. - */ - public ArrayList<SsaBasicBlock> getBlocks() { - return blocks; - } - - /** - * Returns the count of reachable blocks in this method: blocks that have - * predecessors (or are the start block) - * - * @return {@code >= 0;} number of reachable basic blocks - */ - public int getCountReachableBlocks() { - int ret = 0; - - for (SsaBasicBlock b : blocks) { - // Blocks that have been disconnected don't count. - if (b.isReachable()) { - ret++; - } - } - - return ret; - } - - /** - * Computes reachability for all blocks in the method. First clears old - * values from all blocks, then starts with the entry block and walks down - * the control flow graph, marking all blocks it finds as reachable. - */ - public void computeReachability() { - for (SsaBasicBlock block : blocks) { - block.setReachable(0); - } - - ArrayList<SsaBasicBlock> blockList = new ArrayList<SsaBasicBlock>(); - blockList.add(this.getEntryBlock()); - - while (!blockList.isEmpty()) { - SsaBasicBlock block = blockList.remove(0); - if (block.isReachable()) continue; - - block.setReachable(1); - BitSet succs = block.getSuccessors(); - for (int i = succs.nextSetBit(0); i >= 0; - i = succs.nextSetBit(i + 1)) { - blockList.add(blocks.get(i)); - } - } - } - - /** - * Remaps unversioned registers. - * - * @param mapper maps old registers to new. - */ - public void mapRegisters(RegisterMapper mapper) { - for (SsaBasicBlock block : getBlocks()) { - for (SsaInsn insn : block.getInsns()) { - insn.mapRegisters(mapper); - } - } - - registerCount = mapper.getNewRegisterCount(); - spareRegisterBase = registerCount; - } - - /** - * Returns the insn that defines the given register - * @param reg register in question - * @return insn (actual instance from code) that defined this reg or null - * if reg is not defined. - */ - public SsaInsn getDefinitionForRegister(int reg) { - if (backMode) { - throw new RuntimeException("No def list in back mode"); - } - - if (definitionList != null) { - return definitionList[reg]; - } - - definitionList = new SsaInsn[getRegCount()]; - - forEachInsn(new SsaInsn.Visitor() { - public void visitMoveInsn (NormalSsaInsn insn) { - definitionList[insn.getResult().getReg()] = insn; - } - public void visitPhiInsn (PhiInsn phi) { - definitionList[phi.getResult().getReg()] = phi; - } - public void visitNonMoveInsn (NormalSsaInsn insn) { - RegisterSpec result = insn.getResult(); - if (result != null) { - definitionList[insn.getResult().getReg()] = insn; - } - } - }); - - return definitionList[reg]; - } - - /** - * Builds useList and unmodifiableUseList. - */ - private void buildUseList() { - if (backMode) { - throw new RuntimeException("No use list in back mode"); - } - - useList = new ArrayList[registerCount]; - - for (int i = 0; i < registerCount; i++) { - useList[i] = new ArrayList(); - } - - forEachInsn(new SsaInsn.Visitor() { - /** {@inheritDoc} */ - public void visitMoveInsn (NormalSsaInsn insn) { - addToUses(insn); - } - /** {@inheritDoc} */ - public void visitPhiInsn (PhiInsn phi) { - addToUses(phi); - } - /** {@inheritDoc} */ - public void visitNonMoveInsn (NormalSsaInsn insn) { - addToUses(insn); - } - /** - * Adds specified insn to the uses list for all of its sources. - * @param insn {@code non-null;} insn to process - */ - private void addToUses(SsaInsn insn) { - RegisterSpecList rl = insn.getSources(); - int sz = rl.size(); - - for (int i = 0; i < sz; i++) { - useList[rl.get(i).getReg()].add(insn); - } - } - }); - - unmodifiableUseList = new List[registerCount]; - - for (int i = 0; i < registerCount; i++) { - unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]); - } - } - - /** - * Updates the use list for a single change in source register. - * - * @param insn {@code non-null;} insn being changed - * @param oldSource {@code null-ok;} The source that was used, if - * applicable - * @param newSource {@code non-null;} the new source being used - */ - /*package*/ void onSourceChanged(SsaInsn insn, - RegisterSpec oldSource, RegisterSpec newSource) { - if (useList == null) return; - - if (oldSource != null) { - int reg = oldSource.getReg(); - useList[reg].remove(insn); - } - - int reg = newSource.getReg(); - if (useList.length <= reg) { - useList = null; - return; - } - useList[reg].add(insn); - } - - /** - * Updates the use list for a source list change. - * - * @param insn {@code insn non-null;} insn being changed. - * {@code insn.getSources()} must return the new source list. - * @param oldSources {@code null-ok;} list of sources that were - * previously used - */ - /*package*/ void onSourcesChanged(SsaInsn insn, - RegisterSpecList oldSources) { - if (useList == null) return; - - if (oldSources != null) { - removeFromUseList(insn, oldSources); - } - - RegisterSpecList sources = insn.getSources(); - int szNew = sources.size(); - - for (int i = 0; i < szNew; i++) { - int reg = sources.get(i).getReg(); - useList[reg].add(insn); - } - } - - /** - * Removes a given {@code insn} from the use lists for the given - * {@code oldSources} (rather than the sources currently - * returned by insn.getSources()). - * - * @param insn {@code non-null;} insn in question - * @param oldSources {@code null-ok;} registers whose use lists - * {@code insn} should be removed form - */ - private void removeFromUseList(SsaInsn insn, RegisterSpecList oldSources) { - if (oldSources == null) { - return; - } - - int szNew = oldSources.size(); - for (int i = 0; i < szNew; i++) { - if (!useList[oldSources.get(i).getReg()].remove(insn)) { - throw new RuntimeException("use not found"); - } - } - } - - /** - * Adds an insn to both the use and def lists. For use when adding - * a new insn to the method. - * - * @param insn {@code non-null;} insn to add - */ - /*package*/ void onInsnAdded(SsaInsn insn) { - onSourcesChanged(insn, null); - updateOneDefinition(insn, null); - } - - /** - * Removes an instruction from use and def lists. For use during - * instruction removal. - * - * @param insn {@code non-null;} insn to remove - */ - /*package*/ void onInsnRemoved(SsaInsn insn) { - if (useList != null) { - removeFromUseList(insn, insn.getSources()); - } - - RegisterSpec resultReg = insn.getResult(); - if (definitionList != null && resultReg != null) { - definitionList[resultReg.getReg()] = null; - } - } - - /** - * Indicates that the instruction list has changed or the SSA register - * count has increased, so that internal datastructures that rely on - * it should be rebuild. In general, the various other on* methods - * should be called in preference when changes occur if they are - * applicable. - */ - public void onInsnsChanged() { - // Definition list will need to be recomputed - definitionList = null; - - // Use list will need to be recomputed - useList = null; - unmodifiableUseList = null; - } - - /** - * Updates a single definition. - * - * @param insn {@code non-null;} insn who's result should be recorded as - * a definition - * @param oldResult {@code null-ok;} a previous result that should - * be no longer considered a definition by this insn - */ - /*package*/ void updateOneDefinition(SsaInsn insn, - RegisterSpec oldResult) { - if (definitionList == null) return; - - if (oldResult != null) { - int reg = oldResult.getReg(); - definitionList[reg] = null; - } - - RegisterSpec resultReg = insn.getResult(); - - if (resultReg != null) { - int reg = resultReg.getReg(); - - if (definitionList[reg] != null) { - throw new RuntimeException("Duplicate add of insn"); - } else { - definitionList[resultReg.getReg()] = insn; - } - } - } - - /** - * Returns the list of all source uses (not results) for a register. - * - * @param reg register in question - * @return unmodifiable instruction list - */ - public List<SsaInsn> getUseListForRegister(int reg) { - - if (unmodifiableUseList == null) { - buildUseList(); - } - - return unmodifiableUseList[reg]; - } - - /** - * Returns a modifiable copy of the register use list. - * - * @return modifiable copy of the use-list, indexed by register - */ - public ArrayList<SsaInsn>[] getUseListCopy() { - if (useList == null) { - buildUseList(); - } - - ArrayList<SsaInsn>[] useListCopy - = (ArrayList<SsaInsn>[])(new ArrayList[registerCount]); - - for (int i = 0; i < registerCount; i++) { - useListCopy[i] = (ArrayList<SsaInsn>)(new ArrayList(useList[i])); - } - - return useListCopy; - } - - /** - * Checks to see if the given SSA reg is ever associated with a local - * local variable. Each SSA reg may be associated with at most one - * local var. - * - * @param spec {@code non-null;} ssa reg - * @return true if reg is ever associated with a local - */ - public boolean isRegALocal(RegisterSpec spec) { - SsaInsn defn = getDefinitionForRegister(spec.getReg()); - - if (defn == null) { - // version 0 registers are never used as locals - return false; - } - - // Does the definition have a local associated with it? - if (defn.getLocalAssignment() != null) return true; - - // If not, is there a mark-local insn? - for (SsaInsn use : getUseListForRegister(spec.getReg())) { - Insn insn = use.getOriginalRopInsn(); - - if (insn != null - && insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) { - return true; - } - } - - return false; - } - - /** - * Sets the new register count after renaming. - * - * @param newRegCount new register count - */ - /*package*/ void setNewRegCount(int newRegCount) { - registerCount = newRegCount; - spareRegisterBase = registerCount; - onInsnsChanged(); - } - - /** - * Makes a new SSA register. For use after renaming has completed. - * - * @return {@code >=0;} new SSA register. - */ - public int makeNewSsaReg() { - int reg = registerCount++; - spareRegisterBase = registerCount; - onInsnsChanged(); - return reg; - } - - /** - * Visits all insns in this method. - * - * @param visitor {@code non-null;} callback interface - */ - public void forEachInsn(SsaInsn.Visitor visitor) { - for (SsaBasicBlock block : blocks) { - block.forEachInsn(visitor); - } - } - - /** - * Visits each phi insn in this method - * @param v {@code non-null;} callback. - * - */ - public void forEachPhiInsn(PhiInsn.Visitor v) { - for (SsaBasicBlock block : blocks) { - block.forEachPhiInsn(v); - } - } - - - /** - * Walks the basic block tree in depth-first order, calling the visitor - * method once for every block. This depth-first walk may be run forward - * from the method entry point or backwards from the method exit points. - * - * @param reverse true if this should walk backwards from the exit points - * @param v {@code non-null;} callback interface. {@code parent} is set - * unless this is the root node - */ - public void forEachBlockDepthFirst(boolean reverse, - SsaBasicBlock.Visitor v) { - BitSet visited = new BitSet(blocks.size()); - - // We push the parent first, then the child on the stack. - Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>(); - - SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock(); - - if (rootBlock == null) { - // in the case there's no exit block - return; - } - - stack.add(null); // Start with null parent. - stack.add(rootBlock); - - while (stack.size() > 0) { - SsaBasicBlock cur = stack.pop(); - SsaBasicBlock parent = stack.pop(); - - if (!visited.get(cur.getIndex())) { - BitSet children - = reverse ? cur.getPredecessors() : cur.getSuccessors(); - for (int i = children.nextSetBit(0); i >= 0 - ; i = children.nextSetBit(i + 1)) { - stack.add(cur); - stack.add(blocks.get(i)); - } - visited.set(cur.getIndex()); - v.visitBlock(cur, parent); - } - } - } - - /** - * Visits blocks in dom-tree order, starting at the current node. - * The {@code parent} parameter of the Visitor.visitBlock callback - * is currently always set to null. - * - * @param v {@code non-null;} callback interface - */ - public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) { - BitSet visited = new BitSet(getBlocks().size()); - Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>(); - - stack.add(getEntryBlock()); - - while (stack.size() > 0) { - SsaBasicBlock cur = stack.pop(); - ArrayList<SsaBasicBlock> curDomChildren = cur.getDomChildren(); - - if (!visited.get(cur.getIndex())) { - // We walk the tree this way for historical reasons... - for (int i = curDomChildren.size() - 1; i >= 0; i--) { - SsaBasicBlock child = curDomChildren.get(i); - stack.add(child); - } - visited.set(cur.getIndex()); - v.visitBlock(cur, null); - } - } - } - - /** - * Deletes all insns in the set from this method. - * - * @param deletedInsns {@code non-null;} insns to delete - */ - public void deleteInsns(Set<SsaInsn> deletedInsns) { - for (SsaBasicBlock block : getBlocks()) { - ArrayList<SsaInsn> insns = block.getInsns(); - - for (int i = insns.size() - 1; i >= 0; i--) { - SsaInsn insn = insns.get(i); - - if (deletedInsns.contains(insn)) { - onInsnRemoved(insn); - insns.remove(i); - } - } - - // Check to see if we need to add a GOTO - - int insnsSz = insns.size(); - SsaInsn lastInsn = (insnsSz == 0) ? null : insns.get(insnsSz - 1); - - if (block != getExitBlock() && (insnsSz == 0 - || lastInsn.getOriginalRopInsn() == null - || lastInsn.getOriginalRopInsn().getOpcode() - .getBranchingness() == Rop.BRANCH_NONE)) { - // We managed to eat a throwable insn - - Insn gotoInsn = new PlainInsn(Rops.GOTO, - SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY); - insns.add(SsaInsn.makeFromRop(gotoInsn, block)); - - // Remove secondary successors from this block - BitSet succs = block.getSuccessors(); - for (int i = succs.nextSetBit(0); i >= 0; - i = succs.nextSetBit(i + 1)) { - if (i != block.getPrimarySuccessorIndex()) { - block.removeSuccessor(i); - } - } - } - } - } - - /** - * Sets "back-convert mode". Set during back-conversion when registers - * are about to be mapped into a non-SSA namespace. When true, - * use and def lists are unavailable. - */ - public void setBackMode() { - backMode = true; - useList = null; - definitionList = null; - } + /** + * Sets "back-convert mode". Set during back-conversion when registers + * are about to be mapped into a non-SSA namespace. When true, + * use and def lists are unavailable. + */ + public void setBackMode() { + backMode = true; + useList = null; + definitionList = null; + } } diff --git a/dx/src/com/android/jack/dx/ssa/SsaRenamer.java b/dx/src/com/android/jack/dx/ssa/SsaRenamer.java index be9c51b..e1c5d78 100644 --- a/dx/src/com/android/jack/dx/ssa/SsaRenamer.java +++ b/dx/src/com/android/jack/dx/ssa/SsaRenamer.java @@ -60,603 +60,595 @@ import java.util.HashSet; * and used as the initial state for child blocks.<p> */ public class SsaRenamer implements Runnable { - /** debug flag */ - private static final boolean DEBUG = false; + /** debug flag */ + private static final boolean DEBUG = false; + + /** method we're processing */ + private final SsaMethod ssaMeth; + + /** next available SSA register */ + private int nextSsaReg; + + /** the number of original rop registers */ + private final int ropRegCount; + + /** work only on registers above this value */ + private int threshold; + + /** + * indexed by block index; register version state for each block start. + * This list is updated by each dom parent for its children. The only + * sub-arrays that exist at any one time are the start states for blocks + * yet to be processed by a {@code BlockRenamer} instance. + */ + private final RegisterSpec[][] startsForBlocks; + + /** map of SSA register number to debug (local var names) or null of n/a */ + private final ArrayList<LocalItem> ssaRegToLocalItems; + + /** + * maps SSA registers back to the original rop number. Used for + * debug only. + */ + private IntList ssaRegToRopReg; + + /** + * Constructs an instance of the renamer + * + * @param ssaMeth {@code non-null;} un-renamed SSA method that will + * be renamed. + */ + public SsaRenamer(SsaMethod ssaMeth) { + ropRegCount = ssaMeth.getRegCount(); + + this.ssaMeth = ssaMeth; + + /* + * Reserve the first N registers in the SSA register space for + * "version 0" registers. + */ + nextSsaReg = ropRegCount; + threshold = 0; + startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][]; - /** method we're processing */ - private final SsaMethod ssaMeth; + ssaRegToLocalItems = new ArrayList<LocalItem>(); - /** next available SSA register */ - private int nextSsaReg; + if (DEBUG) { + ssaRegToRopReg = new IntList(ropRegCount); + } - /** the number of original rop registers */ - private final int ropRegCount; + /* + * Appel 19.7 + * + * Initialization: + * for each variable a // register i + * Count[a] <- 0 // nextSsaReg, flattened + * Stack[a] <- 0 // versionStack + * push 0 onto Stack[a] + * + */ - /** work only on registers above this value */ - private int threshold; +// top entry for the version stack is version 0 + RegisterSpec[] initialRegMapping = new RegisterSpec[ropRegCount]; + for (int i = 0; i < ropRegCount; i++) { + // everyone starts with a version 0 register + initialRegMapping[i] = RegisterSpec.make(i, Type.VOID); - /** - * indexed by block index; register version state for each block start. - * This list is updated by each dom parent for its children. The only - * sub-arrays that exist at any one time are the start states for blocks - * yet to be processed by a {@code BlockRenamer} instance. - */ - private final RegisterSpec[][] startsForBlocks; + if (DEBUG) { + ssaRegToRopReg.add(i); + } + } + + // Initial state for entry block + startsForBlocks[ssaMeth.getEntryBlockIndex()] = initialRegMapping; + } + + /** + * Constructs an instance of the renamer with threshold set + * + * @param ssaMeth {@code non-null;} un-renamed SSA method that will + * be renamed. + * @param thresh registers below this number are unchanged + */ + public SsaRenamer(SsaMethod ssaMeth, int thresh) { + this(ssaMeth); + threshold = thresh; + } + + /** + * Performs renaming transformation, modifying the method's instructions + * in-place. + */ + @Override + public void run() { + // Rename each block in dom-tree DFS order. + ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() { + @Override + public void visitBlock(SsaBasicBlock block, SsaBasicBlock unused) { + new BlockRenamer(block).process(); + } + }); + + ssaMeth.setNewRegCount(nextSsaReg); + ssaMeth.onInsnsChanged(); + + if (DEBUG) { + System.out.println("SSA\tRop"); + /* + * We're going to compute the version of the rop register + * by keeping a running total of how many times the rop + * register has been mapped. + */ + int[] versions = new int[ropRegCount]; + + int sz = ssaRegToRopReg.size(); + for (int i = 0; i < sz; i++) { + int ropReg = ssaRegToRopReg.get(i); + System.out.println(i + "\t" + ropReg + "[" + versions[ropReg] + "]"); + versions[ropReg]++; + } + } + } + + /** + * Duplicates a RegisterSpec array. + * + * @param orig {@code non-null;} array to duplicate + * @return {@code non-null;} new instance + */ + private static RegisterSpec[] dupArray(RegisterSpec[] orig) { + RegisterSpec[] copy = new RegisterSpec[orig.length]; + + System.arraycopy(orig, 0, copy, 0, orig.length); + + return copy; + } + + /** + * Gets a local variable item for a specified register. + * + * @param ssaReg register in SSA name space + * @return {@code null-ok;} Local variable name or null if none + */ + private LocalItem getLocalForNewReg(int ssaReg) { + if (ssaReg < ssaRegToLocalItems.size()) { + return ssaRegToLocalItems.get(ssaReg); + } else { + return null; + } + } + + /** + * Records a debug (local variable) name for a specified register. + * + * @param ssaReg non-null named register spec in SSA name space + */ + private void setNameForSsaReg(RegisterSpec ssaReg) { + int reg = ssaReg.getReg(); + LocalItem local = ssaReg.getLocalItem(); + + ssaRegToLocalItems.ensureCapacity(reg + 1); + while (ssaRegToLocalItems.size() <= reg) { + ssaRegToLocalItems.add(null); + } - /** map of SSA register number to debug (local var names) or null of n/a */ - private final ArrayList<LocalItem> ssaRegToLocalItems; + ssaRegToLocalItems.set(reg, local); + } + + /** + * Returns true if this SSA register is below the specified threshold. + * Used when most code is already in SSA form, and renaming is needed only + * for registers above a certain threshold. + * + * @param ssaReg the SSA register in question + * @return {@code true} if its register number is below the threshold + */ + private boolean isBelowThresholdRegister(int ssaReg) { + return ssaReg < threshold; + } + + /** + * Returns true if this SSA register is a "version 0" + * register. All version 0 registers are assigned the first N register + * numbers, where N is the count of original rop registers. + * + * @param ssaReg the SSA register in question + * @return true if it is a version 0 register. + */ + private boolean isVersionZeroRegister(int ssaReg) { + return ssaReg < ropRegCount; + } + + /** + * Returns true if a and b are equal or are both null. + * + * @param a null-ok + * @param b null-ok + * @return Returns true if a and b are equal or are both null + */ + private static boolean equalsHandlesNulls(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } + + /** + * Processes all insns in a block and renames their registers + * as appropriate. + */ + private class BlockRenamer implements SsaInsn.Visitor { + /** {@code non-null;} block we're processing. */ + private final SsaBasicBlock block; /** - * maps SSA registers back to the original rop number. Used for - * debug only. + * {@code non-null;} indexed by old register name. The current + * top of the version stack as seen by this block. It's + * initialized from the ending state of its dom parent, + * updated as the block's instructions are processed, and then + * copied to each one of its dom children. */ - private IntList ssaRegToRopReg; + private final RegisterSpec[] currentMapping; /** - * Constructs an instance of the renamer - * - * @param ssaMeth {@code non-null;} un-renamed SSA method that will - * be renamed. + * contains the set of moves we need to keep to preserve local + * var info. All other moves will be deleted. */ - public SsaRenamer(SsaMethod ssaMeth) { - ropRegCount = ssaMeth.getRegCount(); + private final HashSet<SsaInsn> movesToKeep; - this.ssaMeth = ssaMeth; + /** + * maps the set of insns to replace after renaming is finished + * on the block. + */ + private final HashMap<SsaInsn, SsaInsn> insnsToReplace; - /* - * Reserve the first N registers in the SSA register space for - * "version 0" registers. - */ - nextSsaReg = ropRegCount; - threshold = 0; - startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][]; + private final RenamingMapper mapper; - ssaRegToLocalItems = new ArrayList<LocalItem>(); + /** + * Constructs a block renamer instance. Call {@code process} + * to process. + * + * @param block {@code non-null;} block to process + */ + BlockRenamer(final SsaBasicBlock block) { + this.block = block; + currentMapping = startsForBlocks[block.getIndex()]; + movesToKeep = new HashSet<SsaInsn>(); + insnsToReplace = new HashMap<SsaInsn, SsaInsn>(); + mapper = new RenamingMapper(); + + // We don't need our own start state anymore + startsForBlocks[block.getIndex()] = null; + } - if (DEBUG) { - ssaRegToRopReg = new IntList(ropRegCount); + /** + * Provides a register mapping between the old register space + * and the current renaming mapping. The mapping is updated + * as the current block's instructions are processed. + */ + private class RenamingMapper extends RegisterMapper { + public RenamingMapper() { + // This space intentionally left blank. + } + + /** {@inheritDoc} */ + @Override + public int getNewRegisterCount() { + return nextSsaReg; + } + + /** {@inheritDoc} */ + @Override + public RegisterSpec map(RegisterSpec registerSpec) { + if (registerSpec == null) { + return null; } - /* - * Appel 19.7 - * - * Initialization: - * for each variable a // register i - * Count[a] <- 0 // nextSsaReg, flattened - * Stack[a] <- 0 // versionStack - * push 0 onto Stack[a] - * - */ + int reg = registerSpec.getReg(); - // top entry for the version stack is version 0 - RegisterSpec[] initialRegMapping = new RegisterSpec[ropRegCount]; - for (int i = 0; i < ropRegCount; i++) { - // everyone starts with a version 0 register - initialRegMapping[i] = RegisterSpec.make(i, Type.VOID); + // For debugging: assert that the mapped types are compatible. + if (DEBUG) { + RegisterSpec newVersion = currentMapping[reg]; + if (newVersion.getBasicType() != Type.BT_VOID + && registerSpec.getBasicFrameType() != newVersion.getBasicFrameType()) { - if (DEBUG) { - ssaRegToRopReg.add(i); - } + throw new RuntimeException("mapping registers of incompatible types! " + registerSpec + + " " + currentMapping[reg]); + } } - // Initial state for entry block - startsForBlocks[ssaMeth.getEntryBlockIndex()] = initialRegMapping; + return registerSpec.withReg(currentMapping[reg].getReg()); + } } /** - * Constructs an instance of the renamer with threshold set - * - * @param ssaMeth {@code non-null;} un-renamed SSA method that will - * be renamed. - * @param thresh registers below this number are unchanged - */ - public SsaRenamer(SsaMethod ssaMeth, int thresh) { - this(ssaMeth); - threshold = thresh; - } - - /** - * Performs renaming transformation, modifying the method's instructions - * in-place. + * Renames all the variables in this block and inserts appriopriate + * phis in successor blocks. */ - public void run() { - // Rename each block in dom-tree DFS order. - ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() { - public void visitBlock (SsaBasicBlock block, - SsaBasicBlock unused) { - new BlockRenamer(block).process(); - } - }); - - ssaMeth.setNewRegCount(nextSsaReg); - ssaMeth.onInsnsChanged(); + public void process() { + /* + * From Appel: + * + * Rename(n) = + * for each statement S in block n // 'statement' in 'block' + */ - if (DEBUG) { - System.out.println("SSA\tRop"); - /* - * We're going to compute the version of the rop register - * by keeping a running total of how many times the rop - * register has been mapped. - */ - int[] versions = new int[ropRegCount]; - - int sz = ssaRegToRopReg.size(); - for (int i = 0; i < sz; i++) { - int ropReg = ssaRegToRopReg.get(i); - System.out.println(i + "\t" + ropReg + "[" - + versions[ropReg] + "]"); - versions[ropReg]++; - } - } - } +block.forEachInsn(this); - /** - * Duplicates a RegisterSpec array. - * - * @param orig {@code non-null;} array to duplicate - * @return {@code non-null;} new instance - */ - private static RegisterSpec[] dupArray(RegisterSpec[] orig) { - RegisterSpec[] copy = new RegisterSpec[orig.length]; + updateSuccessorPhis(); - System.arraycopy(orig, 0, copy, 0, orig.length); + // Delete all move insns in this block. + ArrayList<SsaInsn> insns = block.getInsns(); + int szInsns = insns.size(); - return copy; - } + for (int i = szInsns - 1; i >= 0; i--) { + SsaInsn insn = insns.get(i); + SsaInsn replaceInsn; - /** - * Gets a local variable item for a specified register. - * - * @param ssaReg register in SSA name space - * @return {@code null-ok;} Local variable name or null if none - */ - private LocalItem getLocalForNewReg(int ssaReg) { - if (ssaReg < ssaRegToLocalItems.size()) { - return ssaRegToLocalItems.get(ssaReg); - } else { - return null; + replaceInsn = insnsToReplace.get(insn); + + if (replaceInsn != null) { + insns.set(i, replaceInsn); + } else if (insn.isNormalMoveInsn() && !movesToKeep.contains(insn)) { + insns.remove(i); } - } + } - /** - * Records a debug (local variable) name for a specified register. - * - * @param ssaReg non-null named register spec in SSA name space - */ - private void setNameForSsaReg(RegisterSpec ssaReg) { - int reg = ssaReg.getReg(); - LocalItem local = ssaReg.getLocalItem(); + // Store the start states for our dom children. + boolean first = true; + for (SsaBasicBlock child : block.getDomChildren()) { + if (child != block) { + // Don't bother duplicating the array for the first child. + RegisterSpec[] childStart = first ? currentMapping : dupArray(currentMapping); - ssaRegToLocalItems.ensureCapacity(reg + 1); - while (ssaRegToLocalItems.size() <= reg) { - ssaRegToLocalItems.add(null); + startsForBlocks[child.getIndex()] = childStart; + first = false; } + } - ssaRegToLocalItems.set(reg, local); + // currentMapping is owned by a child now. } /** - * Returns true if this SSA register is below the specified threshold. - * Used when most code is already in SSA form, and renaming is needed only - * for registers above a certain threshold. + * Enforces a few contraints when a register mapping is added. * - * @param ssaReg the SSA register in question - * @return {@code true} if its register number is below the threshold - */ - private boolean isBelowThresholdRegister(int ssaReg) { - return ssaReg < threshold; - } - - /** - * Returns true if this SSA register is a "version 0" - * register. All version 0 registers are assigned the first N register - * numbers, where N is the count of original rop registers. + * <ol> + * <li> Ensures that all new SSA registers specs in the mapping + * table with the same register number are identical. In effect, once + * an SSA register spec has received or lost a local variable name, + * then every old-namespace register that maps to it should gain or + * lose its local variable name as well. + * <li> Records the local name associated with the + * register so that a register is never associated with more than one + * local. + * <li> ensures that only one SSA register + * at a time is considered to be associated with a local variable. When + * {@code currentMapping} is updated and the newly added element + * is named, strip that name from any other SSA registers. + * </ol> * - * @param ssaReg the SSA register in question - * @return true if it is a version 0 register. + * @param ropReg {@code >= 0;} rop register number + * @param ssaReg {@code non-null;} an SSA register that has just + * been added to {@code currentMapping} */ - private boolean isVersionZeroRegister(int ssaReg) { - return ssaReg < ropRegCount; + private void addMapping(int ropReg, RegisterSpec ssaReg) { + int ssaRegNum = ssaReg.getReg(); + LocalItem ssaRegLocal = ssaReg.getLocalItem(); + + currentMapping[ropReg] = ssaReg; + + /* + * Ensure all SSA register specs with the same reg are identical. + */ + for (int i = currentMapping.length - 1; i >= 0; i--) { + RegisterSpec cur = currentMapping[i]; + + if (ssaRegNum == cur.getReg()) { + currentMapping[i] = ssaReg; + } + } + + // All further steps are for registers with local information. + if (ssaRegLocal == null) { + return; + } + + // Record that this SSA reg has been associated with a local. + setNameForSsaReg(ssaReg); + + // Ensure that no other SSA regs are associated with this local. + for (int i = currentMapping.length - 1; i >= 0; i--) { + RegisterSpec cur = currentMapping[i]; + + if (ssaRegNum != cur.getReg() && ssaRegLocal.equals(cur.getLocalItem())) { + currentMapping[i] = cur.withLocalItem(null); + } + } } /** - * Returns true if a and b are equal or are both null. + * {@inheritDoc} * - * @param a null-ok - * @param b null-ok - * @return Returns true if a and b are equal or are both null + * Phi insns have their result registers renamed. */ - private static boolean equalsHandlesNulls(Object a, Object b) { - return a == b || (a != null && a.equals(b)); + @Override + public void visitPhiInsn(PhiInsn phi) { + /* don't process sources for phi's */ + processResultReg(phi); } /** - * Processes all insns in a block and renames their registers - * as appropriate. + * {@inheritDoc} + * + * Move insns are treated as a simple mapping operation, and + * will later be removed unless they represent a local variable + * assignment. If they represent a local variable assignement, they + * are preserved. */ - private class BlockRenamer implements SsaInsn.Visitor{ - /** {@code non-null;} block we're processing. */ - private final SsaBasicBlock block; - - /** - * {@code non-null;} indexed by old register name. The current - * top of the version stack as seen by this block. It's - * initialized from the ending state of its dom parent, - * updated as the block's instructions are processed, and then - * copied to each one of its dom children. - */ - private final RegisterSpec[] currentMapping; - - /** - * contains the set of moves we need to keep to preserve local - * var info. All other moves will be deleted. + @Override + public void visitMoveInsn(NormalSsaInsn insn) { + /* + * For moves: copy propogate the move if we can, but don't + * if we need to preserve local variable info and the + * result has a different name than the source. + */ + +RegisterSpec ropResult = insn.getResult(); + int ropResultReg = ropResult.getReg(); + int ropSourceReg = insn.getSources().get(0).getReg(); + + insn.mapSourceRegisters(mapper); + int ssaSourceReg = insn.getSources().get(0).getReg(); + + LocalItem sourceLocal = currentMapping[ropSourceReg].getLocalItem(); + LocalItem resultLocal = ropResult.getLocalItem(); + + /* + * A move from a register that's currently associated with a local + * to one that will not be associated with a local does not need + * to be preserved, but the local association should remain. + * Hence, we inherit the sourceLocal where the resultLocal is null. + */ + +LocalItem newLocal = (resultLocal == null) ? sourceLocal : resultLocal; + LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg); + + /* + * If we take the new local, will only one local have ever + * been associated with this SSA reg? + */ + boolean onlyOneAssociatedLocal = + associatedLocal == null || newLocal == null || newLocal.equals(associatedLocal); + + /* + * If we're going to copy-propogate, then the ssa register + * spec that's going to go into the mapping is made up of + * the source register number mapped from above, the type + * of the result, and the name either from the result (if + * specified) or inherited from the existing mapping. + * + * The move source has incomplete type information in null + * object cases, so the result type is used. + */ + RegisterSpec ssaReg = + RegisterSpec.makeLocalOptional(ssaSourceReg, ropResult.getType(), newLocal); + + if (!Optimizer.getPreserveLocals() + || (onlyOneAssociatedLocal && equalsHandlesNulls(newLocal, sourceLocal)) + && threshold == 0) { + /* + * We don't have to keep this move to preserve local + * information. Either the name is the same, or the result + * register spec is unnamed. */ - private final HashSet<SsaInsn> movesToKeep; - /** - * maps the set of insns to replace after renaming is finished - * on the block. +addMapping(ropResultReg, ssaReg); + } else if (onlyOneAssociatedLocal && sourceLocal == null && threshold == 0) { + /* + * The register was previously unnamed. This means that a + * local starts after it's first assignment in SSA form */ - private final HashMap<SsaInsn, SsaInsn> insnsToReplace; - private final RenamingMapper mapper; +RegisterSpecList ssaSources = + RegisterSpecList.make(RegisterSpec.make(ssaReg.getReg(), ssaReg.getType(), newLocal)); - /** - * Constructs a block renamer instance. Call {@code process} - * to process. - * - * @param block {@code non-null;} block to process - */ - BlockRenamer(final SsaBasicBlock block) { - this.block = block; - currentMapping = startsForBlocks[block.getIndex()]; - movesToKeep = new HashSet<SsaInsn>(); - insnsToReplace = new HashMap<SsaInsn, SsaInsn>(); - mapper = new RenamingMapper(); - - // We don't need our own start state anymore - startsForBlocks[block.getIndex()] = null; - } + SsaInsn newInsn = SsaInsn.makeFromRop( + new PlainInsn(Rops.opMarkLocal(ssaReg), SourcePosition.NO_INFO, null, ssaSources), + block); - /** - * Provides a register mapping between the old register space - * and the current renaming mapping. The mapping is updated - * as the current block's instructions are processed. - */ - private class RenamingMapper extends RegisterMapper { - public RenamingMapper() { - // This space intentionally left blank. - } - - /** {@inheritDoc} */ - @Override - public int getNewRegisterCount() { - return nextSsaReg; - } - - /** {@inheritDoc} */ - @Override - public RegisterSpec map(RegisterSpec registerSpec) { - if (registerSpec == null) return null; - - int reg = registerSpec.getReg(); - - // For debugging: assert that the mapped types are compatible. - if (DEBUG) { - RegisterSpec newVersion = currentMapping[reg]; - if (newVersion.getBasicType() != Type.BT_VOID - && registerSpec.getBasicFrameType() - != newVersion.getBasicFrameType()) { - - throw new RuntimeException( - "mapping registers of incompatible types! " - + registerSpec - + " " + currentMapping[reg]); - } - } - - return registerSpec.withReg(currentMapping[reg].getReg()); - } - } - - /** - * Renames all the variables in this block and inserts appriopriate - * phis in successor blocks. - */ - public void process() { - /* - * From Appel: - * - * Rename(n) = - * for each statement S in block n // 'statement' in 'block' - */ - - block.forEachInsn(this); - - updateSuccessorPhis(); - - // Delete all move insns in this block. - ArrayList<SsaInsn> insns = block.getInsns(); - int szInsns = insns.size(); - - for (int i = szInsns - 1; i >= 0 ; i--) { - SsaInsn insn = insns.get(i); - SsaInsn replaceInsn; - - replaceInsn = insnsToReplace.get(insn); - - if (replaceInsn != null) { - insns.set(i, replaceInsn); - } else if (insn.isNormalMoveInsn() - && !movesToKeep.contains(insn)) { - insns.remove(i); - } - } - - // Store the start states for our dom children. - boolean first = true; - for (SsaBasicBlock child : block.getDomChildren()) { - if (child != block) { - // Don't bother duplicating the array for the first child. - RegisterSpec[] childStart = first ? currentMapping - : dupArray(currentMapping); - - startsForBlocks[child.getIndex()] = childStart; - first = false; - } - } - - // currentMapping is owned by a child now. - } + insnsToReplace.put(insn, newInsn); - /** - * Enforces a few contraints when a register mapping is added. - * - * <ol> - * <li> Ensures that all new SSA registers specs in the mapping - * table with the same register number are identical. In effect, once - * an SSA register spec has received or lost a local variable name, - * then every old-namespace register that maps to it should gain or - * lose its local variable name as well. - * <li> Records the local name associated with the - * register so that a register is never associated with more than one - * local. - * <li> ensures that only one SSA register - * at a time is considered to be associated with a local variable. When - * {@code currentMapping} is updated and the newly added element - * is named, strip that name from any other SSA registers. - * </ol> - * - * @param ropReg {@code >= 0;} rop register number - * @param ssaReg {@code non-null;} an SSA register that has just - * been added to {@code currentMapping} + // Just map as above. + addMapping(ropResultReg, ssaReg); + } else { + /* + * Do not copy-propogate, since the two registers have + * two different local-variable names. */ - private void addMapping(int ropReg, RegisterSpec ssaReg) { - int ssaRegNum = ssaReg.getReg(); - LocalItem ssaRegLocal = ssaReg.getLocalItem(); - - currentMapping[ropReg] = ssaReg; - - /* - * Ensure all SSA register specs with the same reg are identical. - */ - for (int i = currentMapping.length - 1; i >= 0; i--) { - RegisterSpec cur = currentMapping[i]; - - if (ssaRegNum == cur.getReg()) { - currentMapping[i] = ssaReg; - } - } - - // All further steps are for registers with local information. - if (ssaRegLocal == null) { - return; - } - - // Record that this SSA reg has been associated with a local. - setNameForSsaReg(ssaReg); - - // Ensure that no other SSA regs are associated with this local. - for (int i = currentMapping.length - 1; i >= 0; i--) { - RegisterSpec cur = currentMapping[i]; - - if (ssaRegNum != cur.getReg() - && ssaRegLocal.equals(cur.getLocalItem())) { - currentMapping[i] = cur.withLocalItem(null); - } - } - } + processResultReg(insn); - /** - * {@inheritDoc} - * - * Phi insns have their result registers renamed. - */ - public void visitPhiInsn(PhiInsn phi) { - /* don't process sources for phi's */ - processResultReg(phi); - } + movesToKeep.add(insn); + } + } - /** - * {@inheritDoc} - * - * Move insns are treated as a simple mapping operation, and - * will later be removed unless they represent a local variable - * assignment. If they represent a local variable assignement, they - * are preserved. - */ - public void visitMoveInsn(NormalSsaInsn insn) { - /* - * For moves: copy propogate the move if we can, but don't - * if we need to preserve local variable info and the - * result has a different name than the source. - */ - - RegisterSpec ropResult = insn.getResult(); - int ropResultReg = ropResult.getReg(); - int ropSourceReg = insn.getSources().get(0).getReg(); - - insn.mapSourceRegisters(mapper); - int ssaSourceReg = insn.getSources().get(0).getReg(); - - LocalItem sourceLocal - = currentMapping[ropSourceReg].getLocalItem(); - LocalItem resultLocal = ropResult.getLocalItem(); - - /* - * A move from a register that's currently associated with a local - * to one that will not be associated with a local does not need - * to be preserved, but the local association should remain. - * Hence, we inherit the sourceLocal where the resultLocal is null. - */ - - LocalItem newLocal - = (resultLocal == null) ? sourceLocal : resultLocal; - LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg); - - /* - * If we take the new local, will only one local have ever - * been associated with this SSA reg? - */ - boolean onlyOneAssociatedLocal - = associatedLocal == null || newLocal == null - || newLocal.equals(associatedLocal); - - /* - * If we're going to copy-propogate, then the ssa register - * spec that's going to go into the mapping is made up of - * the source register number mapped from above, the type - * of the result, and the name either from the result (if - * specified) or inherited from the existing mapping. - * - * The move source has incomplete type information in null - * object cases, so the result type is used. - */ - RegisterSpec ssaReg - = RegisterSpec.makeLocalOptional( - ssaSourceReg, ropResult.getType(), newLocal); - - if (!Optimizer.getPreserveLocals() || (onlyOneAssociatedLocal - && equalsHandlesNulls(newLocal, sourceLocal)) && - threshold == 0) { - /* - * We don't have to keep this move to preserve local - * information. Either the name is the same, or the result - * register spec is unnamed. - */ - - addMapping(ropResultReg, ssaReg); - } else if (onlyOneAssociatedLocal && sourceLocal == null && - threshold == 0) { - /* - * The register was previously unnamed. This means that a - * local starts after it's first assignment in SSA form - */ - - RegisterSpecList ssaSources = RegisterSpecList.make( - RegisterSpec.make(ssaReg.getReg(), - ssaReg.getType(), newLocal)); - - SsaInsn newInsn - = SsaInsn.makeFromRop( - new PlainInsn(Rops.opMarkLocal(ssaReg), - SourcePosition.NO_INFO, null, ssaSources),block); - - insnsToReplace.put(insn, newInsn); - - // Just map as above. - addMapping(ropResultReg, ssaReg); - } else { - /* - * Do not copy-propogate, since the two registers have - * two different local-variable names. - */ - processResultReg(insn); - - movesToKeep.add(insn); - } - } + /** + * {@inheritDoc} + * + * All insns that are not move or phi insns have their source registers + * mapped ot the current mapping. Their result registers are then + * renamed to a new SSA register which is then added to the current + * register mapping. + */ + @Override + public void visitNonMoveInsn(NormalSsaInsn insn) { + /* for each use of some variable X in S */ + insn.mapSourceRegisters(mapper); - /** - * {@inheritDoc} - * - * All insns that are not move or phi insns have their source registers - * mapped ot the current mapping. Their result registers are then - * renamed to a new SSA register which is then added to the current - * register mapping. - */ - public void visitNonMoveInsn(NormalSsaInsn insn) { - /* for each use of some variable X in S */ - insn.mapSourceRegisters(mapper); + processResultReg(insn); + } - processResultReg(insn); - } + /** + * Renames the result register of this insn and updates the + * current register mapping. Does nothing if this insn has no result. + * Applied to all non-move insns. + * + * @param insn insn to process. + */ + void processResultReg(SsaInsn insn) { + RegisterSpec ropResult = insn.getResult(); - /** - * Renames the result register of this insn and updates the - * current register mapping. Does nothing if this insn has no result. - * Applied to all non-move insns. - * - * @param insn insn to process. - */ - void processResultReg(SsaInsn insn) { - RegisterSpec ropResult = insn.getResult(); + if (ropResult == null) { + return; + } - if (ropResult == null) { - return; - } + int ropReg = ropResult.getReg(); + if (isBelowThresholdRegister(ropReg)) { + return; + } - int ropReg = ropResult.getReg(); - if (isBelowThresholdRegister(ropReg)) { - return; - } + insn.changeResultReg(nextSsaReg); + addMapping(ropReg, insn.getResult()); - insn.changeResultReg(nextSsaReg); - addMapping(ropReg, insn.getResult()); + if (DEBUG) { + ssaRegToRopReg.add(ropReg); + } - if (DEBUG) { - ssaRegToRopReg.add(ropReg); - } + nextSsaReg++; + } - nextSsaReg++; + /** + * Updates the phi insns in successor blocks with operands based + * on the current mapping of the rop register the phis represent. + */ + private void updateSuccessorPhis() { + PhiInsn.Visitor visitor = new PhiInsn.Visitor() { + @Override + public void visitPhiInsn(PhiInsn insn) { + int ropReg; + + ropReg = insn.getRopResultReg(); + if (isBelowThresholdRegister(ropReg)) { + return; + } + + /* + * Never add a version 0 register as a phi + * operand. Version 0 registers represent the + * initial register state, and thus are never + * significant. Furthermore, the register liveness + * algorithm doesn't properly count them as "live + * in" at the beginning of the method. + */ + +RegisterSpec stackTop = currentMapping[ropReg]; + if (!isVersionZeroRegister(stackTop.getReg())) { + insn.addPhiOperand(stackTop, block); + } } + }; - /** - * Updates the phi insns in successor blocks with operands based - * on the current mapping of the rop register the phis represent. - */ - private void updateSuccessorPhis() { - PhiInsn.Visitor visitor = new PhiInsn.Visitor() { - public void visitPhiInsn (PhiInsn insn) { - int ropReg; - - ropReg = insn.getRopResultReg(); - if (isBelowThresholdRegister(ropReg)) { - return; - } - - /* - * Never add a version 0 register as a phi - * operand. Version 0 registers represent the - * initial register state, and thus are never - * significant. Furthermore, the register liveness - * algorithm doesn't properly count them as "live - * in" at the beginning of the method. - */ - - RegisterSpec stackTop = currentMapping[ropReg]; - if (!isVersionZeroRegister(stackTop.getReg())) { - insn.addPhiOperand(stackTop, block); - } - } - }; - - BitSet successors = block.getSuccessors(); - for (int i = successors.nextSetBit(0); i >= 0; - i = successors.nextSetBit(i + 1)) { - SsaBasicBlock successor = ssaMeth.getBlocks().get(i); - successor.forEachPhiInsn(visitor); - } - } + BitSet successors = block.getSuccessors(); + for (int i = successors.nextSetBit(0); i >= 0; i = successors.nextSetBit(i + 1)) { + SsaBasicBlock successor = ssaMeth.getBlocks().get(i); + successor.forEachPhiInsn(visitor); + } } + } } diff --git a/dx/src/com/android/jack/dx/ssa/back/FirstFitAllocator.java b/dx/src/com/android/jack/dx/ssa/back/FirstFitAllocator.java index 4fed8f2..3795077 100644 --- a/dx/src/com/android/jack/dx/ssa/back/FirstFitAllocator.java +++ b/dx/src/com/android/jack/dx/ssa/back/FirstFitAllocator.java @@ -26,7 +26,6 @@ import com.android.jack.dx.util.BitIntSet; import com.android.jack.dx.util.IntSet; import java.util.BitSet; -import java.util.ArrayList; /** * Allocates registers via a naive n^2 register allocator. @@ -34,118 +33,112 @@ import java.util.ArrayList; * intelligently with different size register uses. */ public class FirstFitAllocator extends RegisterAllocator { - /** - * If true, allocator places parameters at the top of the frame - * in calling-convention order. - */ - private static final boolean PRESLOT_PARAMS = true; + /** + * If true, allocator places parameters at the top of the frame + * in calling-convention order. + */ + private static final boolean PRESLOT_PARAMS = true; - /** indexed by old reg; the set of old regs we've mapped */ - private final BitSet mapped; + /** indexed by old reg; the set of old regs we've mapped */ + private final BitSet mapped; - /** {@inheritDoc} */ - public FirstFitAllocator( - final SsaMethod ssaMeth, final InterferenceGraph interference) { - super(ssaMeth, interference); + /** {@inheritDoc} */ + public FirstFitAllocator(final SsaMethod ssaMeth, final InterferenceGraph interference) { + super(ssaMeth, interference); - mapped = new BitSet(ssaMeth.getRegCount()); - } + mapped = new BitSet(ssaMeth.getRegCount()); + } + + /** {@inheritDoc} */ + @Override + public boolean wantsParamsMovedHigh() { + return PRESLOT_PARAMS; + } + + /** {@inheritDoc} */ + @Override + public RegisterMapper allocateRegisters() { + int oldRegCount = ssaMeth.getRegCount(); + + BasicRegisterMapper mapper = new BasicRegisterMapper(oldRegCount); - /** {@inheritDoc} */ - @Override - public boolean wantsParamsMovedHigh() { - return PRESLOT_PARAMS; + int nextNewRegister = 0; + + if (PRESLOT_PARAMS) { + /* + * Reserve space for the params at the bottom of the register + * space. Later, we'll flip the params to the end of the register + * space. + */ + +nextNewRegister = ssaMeth.getParamWidth(); } - /** {@inheritDoc} */ - @Override - public RegisterMapper allocateRegisters() { - int oldRegCount = ssaMeth.getRegCount(); + for (int i = 0; i < oldRegCount; i++) { + if (mapped.get(i)) { + // we already got this one + continue; + } + + int maxCategory = getCategoryForSsaReg(i); + IntSet current = new BitIntSet(oldRegCount); - BasicRegisterMapper mapper - = new BasicRegisterMapper(oldRegCount); + interference.mergeInterferenceSet(i, current); - int nextNewRegister = 0; + boolean isPreslotted = false; + int newReg = 0; - if (PRESLOT_PARAMS) { - /* - * Reserve space for the params at the bottom of the register - * space. Later, we'll flip the params to the end of the register - * space. - */ + if (PRESLOT_PARAMS && isDefinitionMoveParam(i)) { + // Any move-param definition must be a NormalSsaInsn + NormalSsaInsn defInsn = (NormalSsaInsn) ssaMeth.getDefinitionForRegister(i); - nextNewRegister = ssaMeth.getParamWidth(); + newReg = paramNumberFromMoveParam(defInsn); + + mapper.addMapping(i, newReg, maxCategory); + isPreslotted = true; + } else { + mapper.addMapping(i, nextNewRegister, maxCategory); + newReg = nextNewRegister; + } + + for (int j = i + 1; j < oldRegCount; j++) { + if (mapped.get(j) || isDefinitionMoveParam(j)) { + continue; } - for (int i = 0; i < oldRegCount; i++) { - if (mapped.get(i)) { - // we already got this one - continue; - } - - int maxCategory = getCategoryForSsaReg(i); - IntSet current = new BitIntSet(oldRegCount); - - interference.mergeInterferenceSet(i, current); - - boolean isPreslotted = false; - int newReg = 0; - - if (PRESLOT_PARAMS && isDefinitionMoveParam(i)) { - // Any move-param definition must be a NormalSsaInsn - NormalSsaInsn defInsn = (NormalSsaInsn) - ssaMeth.getDefinitionForRegister(i); - - newReg = paramNumberFromMoveParam(defInsn); - - mapper.addMapping(i, newReg, maxCategory); - isPreslotted = true; - } else { - mapper.addMapping(i, nextNewRegister, maxCategory); - newReg = nextNewRegister; - } - - for (int j = i + 1; j < oldRegCount; j++) { - if (mapped.get(j) || isDefinitionMoveParam(j)) { - continue; - } - - /* - * If reg j doesn't interfere with the current mapping. - * Also, if this is a pre-slotted method parameter, we - * can't use more than the original param width. - */ - if (!current.has(j) - && !(isPreslotted - && (maxCategory < getCategoryForSsaReg(j)))) { - - interference.mergeInterferenceSet(j, current); - - maxCategory = Math.max(maxCategory, - getCategoryForSsaReg(j)); - - mapper.addMapping(j, newReg, maxCategory); - mapped.set(j); - } - } - - mapped.set(i); - if (!isPreslotted) { - nextNewRegister += maxCategory; - } + /* + * If reg j doesn't interfere with the current mapping. + * Also, if this is a pre-slotted method parameter, we + * can't use more than the original param width. + */ + if (!current.has(j) && !(isPreslotted && (maxCategory < getCategoryForSsaReg(j)))) { + + interference.mergeInterferenceSet(j, current); + + maxCategory = Math.max(maxCategory, getCategoryForSsaReg(j)); + + mapper.addMapping(j, newReg, maxCategory); + mapped.set(j); } + } - return mapper; + mapped.set(i); + if (!isPreslotted) { + nextNewRegister += maxCategory; + } } - /** - * Returns the parameter number that this move-param insn refers to - * @param ndefInsn a move-param insn (otherwise, exceptions will be thrown) - * @return parameter number (offset in the total parameter width) - */ - private int paramNumberFromMoveParam(NormalSsaInsn ndefInsn) { - CstInsn origInsn = (CstInsn) ndefInsn.getOriginalRopInsn(); + return mapper; + } - return ((CstInteger) origInsn.getConstant()).getValue(); - } + /** + * Returns the parameter number that this move-param insn refers to + * @param ndefInsn a move-param insn (otherwise, exceptions will be thrown) + * @return parameter number (offset in the total parameter width) + */ + private int paramNumberFromMoveParam(NormalSsaInsn ndefInsn) { + CstInsn origInsn = (CstInsn) ndefInsn.getOriginalRopInsn(); + + return ((CstInteger) origInsn.getConstant()).getValue(); + } } diff --git a/dx/src/com/android/jack/dx/ssa/back/FirstFitLocalCombiningAllocator.java b/dx/src/com/android/jack/dx/ssa/back/FirstFitLocalCombiningAllocator.java index 65a5d1d..837bee3 100644 --- a/dx/src/com/android/jack/dx/ssa/back/FirstFitLocalCombiningAllocator.java +++ b/dx/src/com/android/jack/dx/ssa/back/FirstFitLocalCombiningAllocator.java @@ -16,7 +16,12 @@ package com.android.jack.dx.ssa.back; -import com.android.jack.dx.rop.code.*; +import com.android.jack.dx.rop.code.CstInsn; +import com.android.jack.dx.rop.code.LocalItem; +import com.android.jack.dx.rop.code.RegOps; +import com.android.jack.dx.rop.code.RegisterSpec; +import com.android.jack.dx.rop.code.RegisterSpecList; +import com.android.jack.dx.rop.code.Rop; import com.android.jack.dx.rop.cst.CstInteger; import com.android.jack.dx.ssa.InterferenceRegisterMapper; import com.android.jack.dx.ssa.NormalSsaInsn; @@ -40,1100 +45,1079 @@ import java.util.TreeMap; * kept together if possible. */ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { - /** local debug flag */ - private static final boolean DEBUG = false; + /** local debug flag */ + private static final boolean DEBUG = false; - /** maps local variable to a list of associated SSA registers */ - private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables; + /** maps local variable to a list of associated SSA registers */ + private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables; - /** list of move-result-pesudo instructions seen in this method */ - private final ArrayList<NormalSsaInsn> moveResultPseudoInsns; + /** list of move-result-pesudo instructions seen in this method */ + private final ArrayList<NormalSsaInsn> moveResultPseudoInsns; - /** list of invoke-range instructions seen in this method */ - private final ArrayList<NormalSsaInsn> invokeRangeInsns; + /** list of invoke-range instructions seen in this method */ + private final ArrayList<NormalSsaInsn> invokeRangeInsns; - /** list of phi instructions seen in this method */ - private final ArrayList<PhiInsn> phiInsns; + /** list of phi instructions seen in this method */ + private final ArrayList<PhiInsn> phiInsns; - /** indexed by SSA reg; the set of SSA regs we've mapped */ - private final BitSet ssaRegsMapped; + /** indexed by SSA reg; the set of SSA regs we've mapped */ + private final BitSet ssaRegsMapped; - /** Register mapper which will be our result */ - private final InterferenceRegisterMapper mapper; + /** Register mapper which will be our result */ + private final InterferenceRegisterMapper mapper; - /** end of rop registers range (starting at 0) reserved for parameters */ - private final int paramRangeEnd; + /** end of rop registers range (starting at 0) reserved for parameters */ + private final int paramRangeEnd; - /** set of rop registers reserved for parameters or local variables */ - private final BitSet reservedRopRegs; + /** set of rop registers reserved for parameters or local variables */ + private final BitSet reservedRopRegs; - /** set of rop registers that have been used by anything */ - private final BitSet usedRopRegs; + /** set of rop registers that have been used by anything */ + private final BitSet usedRopRegs; - /** true if converter should take steps to minimize rop-form registers */ - private final boolean minimizeRegisters; + /** + * Constructs instance. + * + * @param ssaMeth {@code non-null;} method to process + * @param interference non-null interference graph for SSA registers + * @param minimizeRegisters true if converter should take steps to + * minimize rop-form registers + */ + public FirstFitLocalCombiningAllocator(SsaMethod ssaMeth, InterferenceGraph interference, + boolean minimizeRegisters) { + super(ssaMeth, interference); - /** - * Constructs instance. - * - * @param ssaMeth {@code non-null;} method to process - * @param interference non-null interference graph for SSA registers - * @param minimizeRegisters true if converter should take steps to - * minimize rop-form registers + ssaRegsMapped = new BitSet(ssaMeth.getRegCount()); + + mapper = new InterferenceRegisterMapper(interference, ssaMeth.getRegCount()); + + /* + * Reserve space for the params at the bottom of the register + * space. Later, we'll flip the params to the end of the register + * space. */ - public FirstFitLocalCombiningAllocator( - SsaMethod ssaMeth, InterferenceGraph interference, - boolean minimizeRegisters) { - super(ssaMeth, interference); - ssaRegsMapped = new BitSet(ssaMeth.getRegCount()); + paramRangeEnd = ssaMeth.getParamWidth(); - mapper = new InterferenceRegisterMapper( - interference, ssaMeth.getRegCount()); + reservedRopRegs = new BitSet(paramRangeEnd * 2); + reservedRopRegs.set(0, paramRangeEnd); + usedRopRegs = new BitSet(paramRangeEnd * 2); + localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>(); + moveResultPseudoInsns = new ArrayList<NormalSsaInsn>(); + invokeRangeInsns = new ArrayList<NormalSsaInsn>(); + phiInsns = new ArrayList<PhiInsn>(); + } - this.minimizeRegisters = minimizeRegisters; + /** {@inheritDoc} */ + @Override + public boolean wantsParamsMovedHigh() { + return true; + } - /* - * Reserve space for the params at the bottom of the register - * space. Later, we'll flip the params to the end of the register - * space. - */ + /** {@inheritDoc} */ + @Override + public RegisterMapper allocateRegisters() { - paramRangeEnd = ssaMeth.getParamWidth(); + analyzeInstructions(); - reservedRopRegs = new BitSet(paramRangeEnd * 2); - reservedRopRegs.set(0, paramRangeEnd); - usedRopRegs = new BitSet(paramRangeEnd * 2); - localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>(); - moveResultPseudoInsns = new ArrayList<NormalSsaInsn>(); - invokeRangeInsns = new ArrayList<NormalSsaInsn>(); - phiInsns = new ArrayList<PhiInsn>(); + if (DEBUG) { + printLocalVars(); } - /** {@inheritDoc} */ - @Override - public boolean wantsParamsMovedHigh() { - return true; + if (DEBUG) { + System.out.println("--->Mapping local-associated params"); } + handleLocalAssociatedParams(); - /** {@inheritDoc} */ - @Override - public RegisterMapper allocateRegisters() { + if (DEBUG) { + System.out.println("--->Mapping other params"); + } + handleUnassociatedParameters(); - analyzeInstructions(); + if (DEBUG) { + System.out.println("--->Mapping invoke-range"); + } + handleInvokeRangeInsns(); - if (DEBUG) { - printLocalVars(); - } + if (DEBUG) { + System.out.println("--->Mapping local-associated non-params"); + } + handleLocalAssociatedOther(); - if (DEBUG) System.out.println("--->Mapping local-associated params"); - handleLocalAssociatedParams(); + if (DEBUG) { + System.out.println("--->Mapping check-cast results"); + } + handleCheckCastResults(); - if (DEBUG) System.out.println("--->Mapping other params"); - handleUnassociatedParameters(); + if (DEBUG) { + System.out.println("--->Mapping phis"); + } + handlePhiInsns(); - if (DEBUG) System.out.println("--->Mapping invoke-range"); - handleInvokeRangeInsns(); + if (DEBUG) { + System.out.println("--->Mapping others"); + } + handleNormalUnassociated(); + + return mapper; + } + + /** + * Dumps local variable table to stdout for debugging. + */ + private void printLocalVars() { + System.out.println("Printing local vars"); + for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e : localVariables.entrySet()) { + StringBuilder regs = new StringBuilder(); + + regs.append('{'); + regs.append(' '); + for (RegisterSpec reg : e.getValue()) { + regs.append('v'); + regs.append(reg.getReg()); + regs.append(' '); + } + regs.append('}'); + System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs); + } + } + + /** + * Maps all local-associated parameters to rop registers. + */ + private void handleLocalAssociatedParams() { + for (ArrayList<RegisterSpec> ssaRegs : localVariables.values()) { + int sz = ssaRegs.size(); + int paramIndex = -1; + int paramCategory = 0; + + // First, find out if this local variable is a parameter. + for (int i = 0; i < sz; i++) { + RegisterSpec ssaSpec = ssaRegs.get(i); + int ssaReg = ssaSpec.getReg(); - if (DEBUG) { - System.out.println("--->Mapping local-associated non-params"); + paramIndex = getParameterIndexForReg(ssaReg); + + if (paramIndex >= 0) { + paramCategory = ssaSpec.getCategory(); + addMapping(ssaSpec, paramIndex); + break; } - handleLocalAssociatedOther(); + } - if (DEBUG) System.out.println("--->Mapping check-cast results"); - handleCheckCastResults(); + if (paramIndex < 0) { + // This local wasn't a parameter. + continue; + } - if (DEBUG) System.out.println("--->Mapping phis"); - handlePhiInsns(); + // Any remaining local-associated registers will be mapped later. + tryMapRegs(ssaRegs, paramIndex, paramCategory, true); + } + } + + /** + * Gets the parameter index for SSA registers that are method parameters. + * {@code -1} is returned for non-parameter registers. + * + * @param ssaReg {@code >=0;} SSA register to look up + * @return parameter index or {@code -1} if not a parameter + */ + private int getParameterIndexForReg(int ssaReg) { + SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg); + if (defInsn == null) { + return -1; + } - if (DEBUG) System.out.println("--->Mapping others"); - handleNormalUnassociated(); + Rop opcode = defInsn.getOpcode(); - return mapper; + // opcode == null for phi insns. + if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) { + CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn(); + return ((CstInteger) origInsn.getConstant()).getValue(); } - /** - * Dumps local variable table to stdout for debugging. - */ - private void printLocalVars() { - System.out.println("Printing local vars"); - for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e : - localVariables.entrySet()) { - StringBuilder regs = new StringBuilder(); - - regs.append('{'); - regs.append(' '); - for (RegisterSpec reg : e.getValue()) { - regs.append('v'); - regs.append(reg.getReg()); - regs.append(' '); - } - regs.append('}'); - System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs); + return -1; + } + + /** + * Maps all local-associated registers that are not parameters. + * Tries to find an unreserved range that's wide enough for all of + * the SSA registers, and then tries to map them all to that + * range. If not all fit, a new range is tried until all registers + * have been fit. + */ + private void handleLocalAssociatedOther() { + for (ArrayList<RegisterSpec> specs : localVariables.values()) { + int ropReg = paramRangeEnd; + + boolean done = false; + do { + int maxCategory = 1; + + // Compute max category for remaining unmapped registers. + int sz = specs.size(); + for (int i = 0; i < sz; i++) { + RegisterSpec ssaSpec = specs.get(i); + int category = ssaSpec.getCategory(); + if (!ssaRegsMapped.get(ssaSpec.getReg()) && category > maxCategory) { + maxCategory = category; + } } - } - /** - * Maps all local-associated parameters to rop registers. - */ - private void handleLocalAssociatedParams() { - for (ArrayList<RegisterSpec> ssaRegs : localVariables.values()) { - int sz = ssaRegs.size(); - int paramIndex = -1; - int paramCategory = 0; - - // First, find out if this local variable is a parameter. - for (int i = 0; i < sz; i++) { - RegisterSpec ssaSpec = ssaRegs.get(i); - int ssaReg = ssaSpec.getReg(); - - paramIndex = getParameterIndexForReg(ssaReg); - - if (paramIndex >= 0) { - paramCategory = ssaSpec.getCategory(); - addMapping(ssaSpec, paramIndex); - break; - } - } - - if (paramIndex < 0) { - // This local wasn't a parameter. - continue; - } - - // Any remaining local-associated registers will be mapped later. - tryMapRegs(ssaRegs, paramIndex, paramCategory, true); + ropReg = findRopRegForLocal(ropReg, maxCategory); + if (canMapRegs(specs, ropReg)) { + done = tryMapRegs(specs, ropReg, maxCategory, true); } + + // Increment for next call to findRopRegForLocal. + ropReg++; + } while (!done); + } + } + + /** + * Tries to map a list of SSA registers into the a rop reg, marking + * used rop space as reserved. SSA registers that don't fit are left + * unmapped. + * + * @param specs {@code non-null;} SSA registers to attempt to map + * @param ropReg {@code >=0;} rop register to map to + * @param maxAllowedCategory {@code 1..2;} maximum category + * allowed in mapping. + * @param markReserved do so if {@code true} + * @return {@code true} if all registers were mapped, {@code false} + * if some remain unmapped + */ + private boolean tryMapRegs(ArrayList<RegisterSpec> specs, int ropReg, int maxAllowedCategory, + boolean markReserved) { + boolean remaining = false; + for (RegisterSpec spec : specs) { + if (ssaRegsMapped.get(spec.getReg())) { + continue; + } + + boolean succeeded; + succeeded = tryMapReg(spec, ropReg, maxAllowedCategory); + remaining = !succeeded || remaining; + if (succeeded && markReserved) { + // This only needs to be called once really with + // the widest category used, but <shrug> + markReserved(ropReg, spec.getCategory()); + } + } + return !remaining; + } + + /** + * Tries to map an SSA register to a rop register. + * + * @param ssaSpec {@code non-null;} SSA register + * @param ropReg {@code >=0;} rop register + * @param maxAllowedCategory {@code 1..2;} the maximum category + * that the SSA register is allowed to be + * @return {@code true} if map succeeded, {@code false} if not + */ + private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg, int maxAllowedCategory) { + if (ssaSpec.getCategory() <= maxAllowedCategory && !ssaRegsMapped.get(ssaSpec.getReg()) + && canMapReg(ssaSpec, ropReg)) { + addMapping(ssaSpec, ropReg); + return true; } - /** - * Gets the parameter index for SSA registers that are method parameters. - * {@code -1} is returned for non-parameter registers. - * - * @param ssaReg {@code >=0;} SSA register to look up - * @return parameter index or {@code -1} if not a parameter - */ - private int getParameterIndexForReg(int ssaReg) { - SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg); - if (defInsn == null) { - return -1; - } + return false; + } + + /** + * Marks a range of rop registers as "reserved for a local variable." + * + * @param ropReg {@code >= 0;} rop register to reserve + * @param category {@code > 0;} width to reserve + */ + private void markReserved(int ropReg, int category) { + reservedRopRegs.set(ropReg, ropReg + category, true); + } + + /** + * Checks to see if any rop registers in the specified range are reserved + * for local variables or parameters. + * + * @param ropRangeStart {@code >= 0;} lowest rop register + * @param width {@code > 0;} number of rop registers in range. + * @return {@code true} if any register in range is marked reserved + */ + private boolean rangeContainsReserved(int ropRangeStart, int width) { + for (int i = ropRangeStart; i < (ropRangeStart + width); i++) { + if (reservedRopRegs.get(i)) { + return true; + } + } + return false; + } - Rop opcode = defInsn.getOpcode(); + /** + * Finds a range of unreserved rop registers. + * + * @param startReg {@code >= 0;} a rop register to start the search at + * @param width {@code > 0;} the width, in registers, required. + * @return {@code >= 0;} start of available register range. + */ + private int findNextUnreservedRopReg(int startReg, int width) { + int reg; - // opcode == null for phi insns. - if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) { - CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn(); - return ((CstInteger) origInsn.getConstant()).getValue(); - } + reg = reservedRopRegs.nextClearBit(startReg); - return -1; - } + while (true) { + int i = 1; - /** - * Maps all local-associated registers that are not parameters. - * Tries to find an unreserved range that's wide enough for all of - * the SSA registers, and then tries to map them all to that - * range. If not all fit, a new range is tried until all registers - * have been fit. - */ - private void handleLocalAssociatedOther() { - for (ArrayList<RegisterSpec> specs : localVariables.values()) { - int ropReg = paramRangeEnd; - - boolean done = false; - do { - int maxCategory = 1; - - // Compute max category for remaining unmapped registers. - int sz = specs.size(); - for (int i = 0; i < sz; i++) { - RegisterSpec ssaSpec = specs.get(i); - int category = ssaSpec.getCategory(); - if (!ssaRegsMapped.get(ssaSpec.getReg()) - && category > maxCategory) { - maxCategory = category; - } - } - - ropReg = findRopRegForLocal(ropReg, maxCategory); - if (canMapRegs(specs, ropReg)) { - done = tryMapRegs(specs, ropReg, maxCategory, true); - } - - // Increment for next call to findRopRegForLocal. - ropReg++; - } while (!done); - } - } + while (i < width && !reservedRopRegs.get(reg + i)) { + i++; + } - /** - * Tries to map a list of SSA registers into the a rop reg, marking - * used rop space as reserved. SSA registers that don't fit are left - * unmapped. - * - * @param specs {@code non-null;} SSA registers to attempt to map - * @param ropReg {@code >=0;} rop register to map to - * @param maxAllowedCategory {@code 1..2;} maximum category - * allowed in mapping. - * @param markReserved do so if {@code true} - * @return {@code true} if all registers were mapped, {@code false} - * if some remain unmapped - */ - private boolean tryMapRegs( - ArrayList<RegisterSpec> specs, int ropReg, - int maxAllowedCategory, boolean markReserved) { - boolean remaining = false; - for (RegisterSpec spec : specs) { - if (ssaRegsMapped.get(spec.getReg())) { - continue; - } - - boolean succeeded; - succeeded = tryMapReg(spec, ropReg, maxAllowedCategory); - remaining = !succeeded || remaining; - if (succeeded && markReserved) { - // This only needs to be called once really with - // the widest category used, but <shrug> - markReserved(ropReg, spec.getCategory()); - } - } - return !remaining; - } + if (i == width) { + return reg; + } - /** - * Tries to map an SSA register to a rop register. - * - * @param ssaSpec {@code non-null;} SSA register - * @param ropReg {@code >=0;} rop register - * @param maxAllowedCategory {@code 1..2;} the maximum category - * that the SSA register is allowed to be - * @return {@code true} if map succeeded, {@code false} if not - */ - private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg, - int maxAllowedCategory) { - if (ssaSpec.getCategory() <= maxAllowedCategory - && !ssaRegsMapped.get(ssaSpec.getReg()) - && canMapReg(ssaSpec, ropReg)) { - addMapping(ssaSpec, ropReg); - return true; + reg = reservedRopRegs.nextClearBit(reg + i); + } + } + + /** + * Finds a range of rop regs that can be used for local variables. + * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any + * rop register that has not yet been used. + * + * @param startReg {@code >= 0;} a rop register to start the search at + * @param width {@code > 0;} the width, in registers, required. + * @return {@code >= 0;} start of available register range. + */ + private int findRopRegForLocal(int startReg, int width) { + int reg; + + reg = usedRopRegs.nextClearBit(startReg); + + while (true) { + int i = 1; + + while (i < width && !usedRopRegs.get(reg + i)) { + i++; + } + + if (i == width) { + return reg; + } + + reg = usedRopRegs.nextClearBit(reg + i); + } + } + + /** + * Maps any parameter that isn't local-associated, which can happen + * in the case where there is no java debug info. + */ + private void handleUnassociatedParameters() { + int szSsaRegs = ssaMeth.getRegCount(); + + for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) { + if (ssaRegsMapped.get(ssaReg)) { + // We already did this one above + continue; + } + + int paramIndex = getParameterIndexForReg(ssaReg); + + RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg); + if (paramIndex >= 0) { + addMapping(ssaSpec, paramIndex); + } + } + } + + /** + * Handles all insns that want a register range for their sources. + */ + private void handleInvokeRangeInsns() { + for (NormalSsaInsn insn : invokeRangeInsns) { + adjustAndMapSourceRangeRange(insn); + } + } + + /** + * Handles check cast results to reuse the same source register. + * Inserts a move if it can't map the same register to both and the + * check cast is not caught. + */ + private void handleCheckCastResults() { + for (NormalSsaInsn insn : moveResultPseudoInsns) { + RegisterSpec moveRegSpec = insn.getResult(); + int moveReg = moveRegSpec.getReg(); + BitSet predBlocks = insn.getBlock().getPredecessors(); + + // Expect one predecessor block only + if (predBlocks.cardinality() != 1) { + continue; + } + + SsaBasicBlock predBlock = ssaMeth.getBlocks().get(predBlocks.nextSetBit(0)); + ArrayList<SsaInsn> insnList = predBlock.getInsns(); + + /** + * If the predecessor block has a check-cast, it will be the last + * instruction + */ + SsaInsn checkCastInsn = insnList.get(insnList.size() - 1); + if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) { + continue; + } + + RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0); + int checkReg = checkRegSpec.getReg(); + + /** + * See if either register is already mapped. Most likely the move + * result will be mapped already since the cast result is stored + * in a local variable. + */ + int category = checkRegSpec.getCategory(); + boolean moveMapped = ssaRegsMapped.get(moveReg); + boolean checkMapped = ssaRegsMapped.get(checkReg); + if (moveMapped & !checkMapped) { + int moveRopReg = mapper.oldToNew(moveReg); + checkMapped = tryMapReg(checkRegSpec, moveRopReg, category); + } + if (checkMapped & !moveMapped) { + int checkRopReg = mapper.oldToNew(checkReg); + moveMapped = tryMapReg(moveRegSpec, checkRopReg, category); + } + + // Map any unmapped registers to anything available + if (!moveMapped || !checkMapped) { + int ropReg = findNextUnreservedRopReg(paramRangeEnd, category); + ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>(2); + ssaRegs.add(moveRegSpec); + ssaRegs.add(checkRegSpec); + + while (!tryMapRegs(ssaRegs, ropReg, category, false)) { + ropReg = findNextUnreservedRopReg(ropReg + 1, category); } - - return false; + } + + /* + * If source and result have a different mapping, insert a move so + * they can have the same mapping. Don't do this if the check cast + * is caught, since it will overwrite a potentially live value. + */ + boolean hasExceptionHandlers = checkCastInsn.getOriginalRopInsn().getCatches().size() != 0; + int moveRopReg = mapper.oldToNew(moveReg); + int checkRopReg = mapper.oldToNew(checkReg); + if (moveRopReg != checkRopReg && !hasExceptionHandlers) { + ((NormalSsaInsn) checkCastInsn).changeOneSource(0, + insertMoveBefore(checkCastInsn, checkRegSpec)); + addMapping(checkCastInsn.getSources().get(0), moveRopReg); + } } - - /** - * Marks a range of rop registers as "reserved for a local variable." - * - * @param ropReg {@code >= 0;} rop register to reserve - * @param category {@code > 0;} width to reserve - */ - private void markReserved(int ropReg, int category) { - reservedRopRegs.set(ropReg, ropReg + category, true); + } + + /** + * Handles all phi instructions, trying to map them to a common register. + */ + private void handlePhiInsns() { + for (PhiInsn insn : phiInsns) { + processPhiInsn(insn); + } + } + + /** + * Maps all non-parameter, non-local variable registers. + */ + private void handleNormalUnassociated() { + int szSsaRegs = ssaMeth.getRegCount(); + + for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) { + if (ssaRegsMapped.get(ssaReg)) { + // We already did this one + continue; + } + + RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg); + + if (ssaSpec == null) { + continue; + } + + int category = ssaSpec.getCategory(); + // Find a rop reg that does not interfere + int ropReg = findNextUnreservedRopReg(paramRangeEnd, category); + while (!canMapReg(ssaSpec, ropReg)) { + ropReg = findNextUnreservedRopReg(ropReg + 1, category); + } + + addMapping(ssaSpec, ropReg); } + } + + /** + * Checks to see if a list of SSA registers can all be mapped into + * the same rop reg. Ignores registers that have already been mapped, + * and checks the interference graph and ensures the range does not + * cross the parameter range. + * + * @param specs {@code non-null;} SSA registers to check + * @param ropReg {@code >=0;} rop register to check mapping to + * @return {@code true} if all unmapped registers can be mapped + */ + private boolean canMapRegs(ArrayList<RegisterSpec> specs, int ropReg) { + for (RegisterSpec spec : specs) { + if (ssaRegsMapped.get(spec.getReg())) { + continue; + } + if (!canMapReg(spec, ropReg)) { + return false; + } + } + return true; + } + + /** + * Checks to see if {@code ssaSpec} can be mapped to + * {@code ropReg}. Checks interference graph and ensures + * the range does not cross the parameter range. + * + * @param ssaSpec {@code non-null;} SSA spec + * @param ropReg prosepctive new-namespace reg + * @return {@code true} if mapping is possible + */ + private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) { + int category = ssaSpec.getCategory(); + return !(spansParamRange(ropReg, category) || mapper.interferes(ssaSpec, ropReg)); + } + + /** + * Returns true if the specified rop register + category + * will cross the boundry between the lower {@code paramWidth} + * registers reserved for method params and the upper registers. We cannot + * allocate a register that spans the param block and the normal block, + * because we will be moving the param block to high registers later. + * + * @param ssaReg register in new namespace + * @param category width that the register will have + * @return {@code true} in the case noted above + */ + private boolean spansParamRange(int ssaReg, int category) { + return ((ssaReg < paramRangeEnd) && ((ssaReg + category) > paramRangeEnd)); + } + + /** + * Analyze each instruction and find out all the local variable assignments + * and move-result-pseudo/invoke-range instrucitons. + */ + private void analyzeInstructions() { + ssaMeth.forEachInsn(new SsaInsn.Visitor() { + /** {@inheritDoc} */ + @Override + public void visitMoveInsn(NormalSsaInsn insn) { + processInsn(insn); + } + + /** {@inheritDoc} */ + @Override + public void visitPhiInsn(PhiInsn insn) { + processInsn(insn); + } + + /** {@inheritDoc} */ + @Override + public void visitNonMoveInsn(NormalSsaInsn insn) { + processInsn(insn); + } + + /** + * This method collects three types of instructions: + * + * 1) Adds a local variable assignment to the + * {@code localVariables} map. + * 2) Add move-result-pseudo to the + * {@code moveResultPseudoInsns} list. + * 3) Add invoke-range to the + * {@code invokeRangeInsns} list. + * + * @param insn {@code non-null;} insn that may represent a + * local variable assignment + */ + private void processInsn(SsaInsn insn) { + RegisterSpec assignment; + assignment = insn.getLocalAssignment(); + + if (assignment != null) { + LocalItem local = assignment.getLocalItem(); + + ArrayList<RegisterSpec> regList = localVariables.get(local); + + if (regList == null) { + regList = new ArrayList<RegisterSpec>(); + localVariables.put(local, regList); + } + + regList.add(assignment); + } - /** - * Checks to see if any rop registers in the specified range are reserved - * for local variables or parameters. - * - * @param ropRangeStart {@code >= 0;} lowest rop register - * @param width {@code > 0;} number of rop registers in range. - * @return {@code true} if any register in range is marked reserved - */ - private boolean rangeContainsReserved(int ropRangeStart, int width) { - for (int i = ropRangeStart; i < (ropRangeStart + width); i++) { - if (reservedRopRegs.get(i)) { - return true; - } + if (insn instanceof NormalSsaInsn) { + if (insn.getOpcode().getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { + moveResultPseudoInsns.add((NormalSsaInsn) insn); + } else if (Optimizer.getAdvice().requiresSourcesInOrder( + insn.getOriginalRopInsn().getOpcode(), insn.getSources())) { + invokeRangeInsns.add((NormalSsaInsn) insn); + } + } else if (insn instanceof PhiInsn) { + phiInsns.add((PhiInsn) insn); } - return false; + + } + }); + } + + /** + * Adds a mapping from an SSA register to a rop register. + * {@link #canMapReg} should have already been called. + * + * @param ssaSpec {@code non-null;} SSA register to map from + * @param ropReg {@code >=0;} rop register to map to + */ + private void addMapping(RegisterSpec ssaSpec, int ropReg) { + int ssaReg = ssaSpec.getReg(); + + // An assertion. + if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) { + throw new RuntimeException("attempt to add invalid register mapping"); } - /** - * Returns true if given rop register represents the {@code this} pointer - * for a non-static method. - * - * @param startReg rop register - * @return true if the "this" pointer is located here. - */ - private boolean isThisPointerReg(int startReg) { - // "this" is always the first parameter. - return startReg == 0 && !ssaMeth.isStatic(); + if (DEBUG) { + System.out.printf("Add mapping s%d -> v%d c:%d\n", ssaSpec.getReg(), ropReg, + ssaSpec.getCategory()); } - /** - * Finds a range of unreserved rop registers. - * - * @param startReg {@code >= 0;} a rop register to start the search at - * @param width {@code > 0;} the width, in registers, required. - * @return {@code >= 0;} start of available register range. - */ - private int findNextUnreservedRopReg(int startReg, int width) { - int reg; + int category = ssaSpec.getCategory(); + mapper.addMapping(ssaSpec.getReg(), ropReg, category); + ssaRegsMapped.set(ssaReg); + usedRopRegs.set(ropReg, ropReg + category); + } - reg = reservedRopRegs.nextClearBit(startReg); - while (true) { - int i = 1; + /** + * Maps the source registers of the specified instruction such that they + * will fall in a contiguous range in rop form. Moves are inserted as + * necessary to allow the range to be allocated. + * + * @param insn {@code non-null;} insn whos sources to process + */ + private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) { + int newRegStart = findRangeAndAdjust(insn); - while (i < width && !reservedRopRegs.get(reg + i)) { - i++; - } + RegisterSpecList sources = insn.getSources(); + int szSources = sources.size(); + int nextRopReg = newRegStart; - if (i == width) { - return reg; - } + for (int i = 0; i < szSources; i++) { + RegisterSpec source = sources.get(i); + int sourceReg = source.getReg(); + int category = source.getCategory(); + int curRopReg = nextRopReg; + nextRopReg += category; - reg = reservedRopRegs.nextClearBit(reg + i); - } - } + if (ssaRegsMapped.get(sourceReg)) { + continue; + } - /** - * Finds a range of rop regs that can be used for local variables. - * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any - * rop register that has not yet been used. - * - * @param startReg {@code >= 0;} a rop register to start the search at - * @param width {@code > 0;} the width, in registers, required. - * @return {@code >= 0;} start of available register range. - */ - private int findRopRegForLocal(int startReg, int width) { - int reg; + LocalItem localItem = getLocalItemForReg(sourceReg); + addMapping(source, curRopReg); - reg = usedRopRegs.nextClearBit(startReg); + if (localItem != null) { + markReserved(curRopReg, category); + ArrayList<RegisterSpec> similarRegisters = localVariables.get(localItem); - while (true) { - int i = 1; + int szSimilar = similarRegisters.size(); - while (i < width && !usedRopRegs.get(reg + i)) { - i++; - } + /* + * Try to map all SSA registers also associated with + * this local. + */ + for (int j = 0; j < szSimilar; j++) { + RegisterSpec similarSpec = similarRegisters.get(j); + int similarReg = similarSpec.getReg(); - if (i == width) { - return reg; - } + // Don't map anything that's also a source. + if (-1 != sources.indexOfRegister(similarReg)) { + continue; + } - reg = usedRopRegs.nextClearBit(reg + i); + // Registers left unmapped will get handled later. + tryMapReg(similarSpec, curRopReg, category); } + } } - - /** - * Maps any parameter that isn't local-associated, which can happen - * in the case where there is no java debug info. - */ - private void handleUnassociatedParameters() { - int szSsaRegs = ssaMeth.getRegCount(); - - for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) { - if (ssaRegsMapped.get(ssaReg)) { - // We already did this one above - continue; - } - - int paramIndex = getParameterIndexForReg(ssaReg); - - RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg); - if (paramIndex >= 0) { - addMapping(ssaSpec, paramIndex); - } - } + } + + /** + * Find a contiguous rop register range that fits the specified + * instruction's sources. First, try to center the range around + * sources that have already been mapped to rop registers. If that fails, + * just find a new contiguous range that doesn't interfere. + * + * @param insn {@code non-null;} the insn whose sources need to + * fit. Must be last insn in basic block. + * @return {@code >= 0;} rop register of start of range + */ + private int findRangeAndAdjust(NormalSsaInsn insn) { + RegisterSpecList sources = insn.getSources(); + int szSources = sources.size(); + // the category for each source index + int categoriesForIndex[] = new int[szSources]; + int rangeLength = 0; + + // Compute rangeLength and categoriesForIndex + for (int i = 0; i < szSources; i++) { + int category = sources.get(i).getCategory(); + categoriesForIndex[i] = category; + rangeLength += categoriesForIndex[i]; } - /** - * Handles all insns that want a register range for their sources. + // the highest score of fits tried so far + int maxScore = Integer.MIN_VALUE; + // the high scoring range's start + int resultRangeStart = -1; + // by source index: set of sources needing moves in high scoring plan + BitSet resultMovesRequired = null; + + /* + * First, go through each source that's already been mapped. Try + * to center the range around the rop register this source is mapped + * to. */ - private void handleInvokeRangeInsns() { - for (NormalSsaInsn insn : invokeRangeInsns) { - adjustAndMapSourceRangeRange(insn); - } - } + int rangeStartOffset = 0; + for (int i = 0; i < szSources; i++) { + int ssaCenterReg = sources.get(i).getReg(); - /** - * Handles check cast results to reuse the same source register. - * Inserts a move if it can't map the same register to both and the - * check cast is not caught. - */ - private void handleCheckCastResults() { - for (NormalSsaInsn insn : moveResultPseudoInsns) { - RegisterSpec moveRegSpec = insn.getResult(); - int moveReg = moveRegSpec.getReg(); - BitSet predBlocks = insn.getBlock().getPredecessors(); - - // Expect one predecessor block only - if (predBlocks.cardinality() != 1) { - continue; - } - - SsaBasicBlock predBlock = - ssaMeth.getBlocks().get(predBlocks.nextSetBit(0)); - ArrayList<SsaInsn> insnList = predBlock.getInsns(); - - /** - * If the predecessor block has a check-cast, it will be the last - * instruction - */ - SsaInsn checkCastInsn = insnList.get(insnList.size() - 1); - if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) { - continue; - } - - RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0); - int checkReg = checkRegSpec.getReg(); - - /** - * See if either register is already mapped. Most likely the move - * result will be mapped already since the cast result is stored - * in a local variable. - */ - int category = checkRegSpec.getCategory(); - boolean moveMapped = ssaRegsMapped.get(moveReg); - boolean checkMapped = ssaRegsMapped.get(checkReg); - if (moveMapped & !checkMapped) { - int moveRopReg = mapper.oldToNew(moveReg); - checkMapped = tryMapReg(checkRegSpec, moveRopReg, category); - } - if (checkMapped & !moveMapped) { - int checkRopReg = mapper.oldToNew(checkReg); - moveMapped = tryMapReg(moveRegSpec, checkRopReg, category); - } - - // Map any unmapped registers to anything available - if (!moveMapped || !checkMapped) { - int ropReg = findNextUnreservedRopReg(paramRangeEnd, category); - ArrayList<RegisterSpec> ssaRegs = - new ArrayList<RegisterSpec>(2); - ssaRegs.add(moveRegSpec); - ssaRegs.add(checkRegSpec); - - while (!tryMapRegs(ssaRegs, ropReg, category, false)) { - ropReg = findNextUnreservedRopReg(ropReg + 1, category); - } - } - - /* - * If source and result have a different mapping, insert a move so - * they can have the same mapping. Don't do this if the check cast - * is caught, since it will overwrite a potentially live value. - */ - boolean hasExceptionHandlers = - checkCastInsn.getOriginalRopInsn().getCatches().size() != 0; - int moveRopReg = mapper.oldToNew(moveReg); - int checkRopReg = mapper.oldToNew(checkReg); - if (moveRopReg != checkRopReg && !hasExceptionHandlers) { - ((NormalSsaInsn) checkCastInsn).changeOneSource(0, - insertMoveBefore(checkCastInsn, checkRegSpec)); - addMapping(checkCastInsn.getSources().get(0), moveRopReg); - } - } - } + if (i != 0) { + rangeStartOffset -= categoriesForIndex[i - 1]; + } + if (!ssaRegsMapped.get(ssaCenterReg)) { + continue; + } - /** - * Handles all phi instructions, trying to map them to a common register. - */ - private void handlePhiInsns() { - for (PhiInsn insn : phiInsns) { - processPhiInsn(insn); - } - } + int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset; - /** - * Maps all non-parameter, non-local variable registers. - */ - private void handleNormalUnassociated() { - int szSsaRegs = ssaMeth.getRegCount(); + if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) { + continue; + } - for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) { - if (ssaRegsMapped.get(ssaReg)) { - // We already did this one - continue; - } + BitSet curMovesRequired = new BitSet(szSources); - RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg); + int fitWidth = fitPlanForRange(rangeStart, insn, categoriesForIndex, curMovesRequired); - if (ssaSpec == null) continue; + if (fitWidth < 0) { + continue; + } - int category = ssaSpec.getCategory(); - // Find a rop reg that does not interfere - int ropReg = findNextUnreservedRopReg(paramRangeEnd, category); - while (!canMapReg(ssaSpec, ropReg)) { - ropReg = findNextUnreservedRopReg(ropReg + 1, category); - } + int score = fitWidth - curMovesRequired.cardinality(); - addMapping(ssaSpec, ropReg); - } - } + if (score > maxScore) { + maxScore = score; + resultRangeStart = rangeStart; + resultMovesRequired = curMovesRequired; + } - /** - * Checks to see if a list of SSA registers can all be mapped into - * the same rop reg. Ignores registers that have already been mapped, - * and checks the interference graph and ensures the range does not - * cross the parameter range. - * - * @param specs {@code non-null;} SSA registers to check - * @param ropReg {@code >=0;} rop register to check mapping to - * @return {@code true} if all unmapped registers can be mapped - */ - private boolean canMapRegs(ArrayList<RegisterSpec> specs, int ropReg) { - for (RegisterSpec spec : specs) { - if (ssaRegsMapped.get(spec.getReg())) continue; - if (!canMapReg(spec, ropReg)) return false; - } - return true; + if (fitWidth == rangeLength) { + // We can't do any better than this, so stop here + break; + } } - /** - * Checks to see if {@code ssaSpec} can be mapped to - * {@code ropReg}. Checks interference graph and ensures - * the range does not cross the parameter range. - * - * @param ssaSpec {@code non-null;} SSA spec - * @param ropReg prosepctive new-namespace reg - * @return {@code true} if mapping is possible + /* + * If we were unable to find a plan for a fit centered around + * an already-mapped source, just try to find a range of + * registers we can move the range into. */ - private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) { - int category = ssaSpec.getCategory(); - return !(spansParamRange(ropReg, category) - || mapper.interferes(ssaSpec, ropReg)); - } - /** - * Returns true if the specified rop register + category - * will cross the boundry between the lower {@code paramWidth} - * registers reserved for method params and the upper registers. We cannot - * allocate a register that spans the param block and the normal block, - * because we will be moving the param block to high registers later. - * - * @param ssaReg register in new namespace - * @param category width that the register will have - * @return {@code true} in the case noted above - */ - private boolean spansParamRange(int ssaReg, int category) { - return ((ssaReg < paramRangeEnd) - && ((ssaReg + category) > paramRangeEnd)); - } +if (resultRangeStart == -1) { + resultMovesRequired = new BitSet(szSources); - /** - * Analyze each instruction and find out all the local variable assignments - * and move-result-pseudo/invoke-range instrucitons. - */ - private void analyzeInstructions() { - ssaMeth.forEachInsn(new SsaInsn.Visitor() { - /** {@inheritDoc} */ - public void visitMoveInsn(NormalSsaInsn insn) { - processInsn(insn); - } - - /** {@inheritDoc} */ - public void visitPhiInsn(PhiInsn insn) { - processInsn(insn); - } - - /** {@inheritDoc} */ - public void visitNonMoveInsn(NormalSsaInsn insn) { - processInsn(insn); - } - - /** - * This method collects three types of instructions: - * - * 1) Adds a local variable assignment to the - * {@code localVariables} map. - * 2) Add move-result-pseudo to the - * {@code moveResultPseudoInsns} list. - * 3) Add invoke-range to the - * {@code invokeRangeInsns} list. - * - * @param insn {@code non-null;} insn that may represent a - * local variable assignment - */ - private void processInsn(SsaInsn insn) { - RegisterSpec assignment; - assignment = insn.getLocalAssignment(); - - if (assignment != null) { - LocalItem local = assignment.getLocalItem(); - - ArrayList<RegisterSpec> regList - = localVariables.get(local); - - if (regList == null) { - regList = new ArrayList<RegisterSpec>(); - localVariables.put(local, regList); - } - - regList.add(assignment); - } - - if (insn instanceof NormalSsaInsn) { - if (insn.getOpcode().getOpcode() == - RegOps.MOVE_RESULT_PSEUDO) { - moveResultPseudoInsns.add((NormalSsaInsn) insn); - } else if (Optimizer.getAdvice().requiresSourcesInOrder( - insn.getOriginalRopInsn().getOpcode(), - insn.getSources())) { - invokeRangeInsns.add((NormalSsaInsn) insn); - } - } else if (insn instanceof PhiInsn) { - phiInsns.add((PhiInsn) insn); - } - - } - }); + resultRangeStart = + findAnyFittingRange(insn, rangeLength, categoriesForIndex, resultMovesRequired); } - /** - * Adds a mapping from an SSA register to a rop register. - * {@link #canMapReg} should have already been called. - * - * @param ssaSpec {@code non-null;} SSA register to map from - * @param ropReg {@code >=0;} rop register to map to + /* + * Now, insert any moves required. */ - private void addMapping(RegisterSpec ssaSpec, int ropReg) { - int ssaReg = ssaSpec.getReg(); - - // An assertion. - if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) { - throw new RuntimeException( - "attempt to add invalid register mapping"); - } - - if (DEBUG) { - System.out.printf("Add mapping s%d -> v%d c:%d\n", - ssaSpec.getReg(), ropReg, ssaSpec.getCategory()); - } - int category = ssaSpec.getCategory(); - mapper.addMapping(ssaSpec.getReg(), ropReg, category); - ssaRegsMapped.set(ssaReg); - usedRopRegs.set(ropReg, ropReg + category); +for (int i = resultMovesRequired.nextSetBit(0); i >= 0; + i = resultMovesRequired.nextSetBit(i + 1)) { + insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i))); } - - /** - * Maps the source registers of the specified instruction such that they - * will fall in a contiguous range in rop form. Moves are inserted as - * necessary to allow the range to be allocated. - * - * @param insn {@code non-null;} insn whos sources to process - */ - private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) { - int newRegStart = findRangeAndAdjust(insn); - - RegisterSpecList sources = insn.getSources(); - int szSources = sources.size(); - int nextRopReg = newRegStart; - - for (int i = 0; i < szSources; i++) { - RegisterSpec source = sources.get(i); - int sourceReg = source.getReg(); - int category = source.getCategory(); - int curRopReg = nextRopReg; - nextRopReg += category; - - if (ssaRegsMapped.get(sourceReg)) { - continue; - } - - LocalItem localItem = getLocalItemForReg(sourceReg); - addMapping(source, curRopReg); - - if (localItem != null) { - markReserved(curRopReg, category); - ArrayList<RegisterSpec> similarRegisters - = localVariables.get(localItem); - - int szSimilar = similarRegisters.size(); - - /* - * Try to map all SSA registers also associated with - * this local. - */ - for (int j = 0; j < szSimilar; j++) { - RegisterSpec similarSpec = similarRegisters.get(j); - int similarReg = similarSpec.getReg(); - - // Don't map anything that's also a source. - if (-1 != sources.indexOfRegister(similarReg)) { - continue; - } - - // Registers left unmapped will get handled later. - tryMapReg(similarSpec, curRopReg, category); - } - } - } + return resultRangeStart; + } + + /** + * Finds an unreserved range that will fit the sources of the + * specified instruction. Does not bother trying to center the range + * around an already-mapped source register; + * + * @param insn {@code non-null;} insn to build range for + * @param rangeLength {@code >=0;} length required in register units + * @param categoriesForIndex {@code non-null;} indexed by source index; + * the category for each source + * @param outMovesRequired {@code non-null;} an output parameter indexed by + * source index that will contain the set of sources which need + * moves inserted + * @return the rop register that starts the fitting range + */ + private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength, int[] categoriesForIndex, + BitSet outMovesRequired) { + int rangeStart = paramRangeEnd; + while (true) { + rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength); + int fitWidth = fitPlanForRange(rangeStart, insn, categoriesForIndex, outMovesRequired); + + if (fitWidth >= 0) { + break; + } + rangeStart++; + outMovesRequired.clear(); } - - /** - * Find a contiguous rop register range that fits the specified - * instruction's sources. First, try to center the range around - * sources that have already been mapped to rop registers. If that fails, - * just find a new contiguous range that doesn't interfere. - * - * @param insn {@code non-null;} the insn whose sources need to - * fit. Must be last insn in basic block. - * @return {@code >= 0;} rop register of start of range - */ - private int findRangeAndAdjust(NormalSsaInsn insn) { - RegisterSpecList sources = insn.getSources(); - int szSources = sources.size(); - // the category for each source index - int categoriesForIndex[] = new int[szSources]; - int rangeLength = 0; - - // Compute rangeLength and categoriesForIndex - for (int i = 0; i < szSources; i++) { - int category = sources.get(i).getCategory(); - categoriesForIndex[i] = category; - rangeLength += categoriesForIndex[i]; - } - - // the highest score of fits tried so far - int maxScore = Integer.MIN_VALUE; - // the high scoring range's start - int resultRangeStart = -1; - // by source index: set of sources needing moves in high scoring plan - BitSet resultMovesRequired = null; - + return rangeStart; + } + + /** + * Attempts to build a plan for fitting a range of sources into rop + * registers. + * + * @param ropReg {@code >= 0;} rop reg that begins range + * @param insn {@code non-null;} insn to plan range for + * @param categoriesForIndex {@code non-null;} indexed by source index; + * the category for each source + * @param outMovesRequired {@code non-null;} an output parameter indexed by + * source index that will contain the set of sources which need + * moves inserted + * @return the width of the fit that that does not involve added moves or + * {@code -1} if "no fit possible" + */ + private int fitPlanForRange(int ropReg, NormalSsaInsn insn, int[] categoriesForIndex, + BitSet outMovesRequired) { + RegisterSpecList sources = insn.getSources(); + int szSources = sources.size(); + int fitWidth = 0; + IntSet liveOut = insn.getBlock().getLiveOutRegs(); + RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut); + + // An SSA reg may only be mapped into a range once. + BitSet seen = new BitSet(ssaMeth.getRegCount()); + + for (int i = 0; i < szSources; i++) { + RegisterSpec ssaSpec = sources.get(i); + int ssaReg = ssaSpec.getReg(); + int category = categoriesForIndex[i]; + + if (i != 0) { + ropReg += categoriesForIndex[i - 1]; + } + + if (ssaRegsMapped.get(ssaReg) && mapper.oldToNew(ssaReg) == ropReg) { + // This is a register that is already mapped appropriately. + fitWidth += category; + } else if (rangeContainsReserved(ropReg, category)) { + fitWidth = -1; + break; + } else if (!ssaRegsMapped.get(ssaReg) && canMapReg(ssaSpec, ropReg) && !seen.get(ssaReg)) { + // This is a register that can be mapped appropriately. + fitWidth += category; + } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category) + && !mapper.areAnyPinned(sources, ropReg, category)) { /* - * First, go through each source that's already been mapped. Try - * to center the range around the rop register this source is mapped - * to. + * This is a source that can be moved. We can insert a + * move as long as: + * + * * no SSA register pinned to the desired rop reg + * is live out on the block + * + * * no SSA register pinned to desired rop reg is + * a source of this insn (since this may require + * overlapping moves, which we can't presently handle) */ - int rangeStartOffset = 0; - for (int i = 0; i < szSources; i++) { - int ssaCenterReg = sources.get(i).getReg(); - - if (i != 0) { - rangeStartOffset -= categoriesForIndex[i - 1]; - } - if (!ssaRegsMapped.get(ssaCenterReg)) { - continue; - } - - int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset; - - if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) { - continue; - } - - BitSet curMovesRequired = new BitSet(szSources); - int fitWidth - = fitPlanForRange(rangeStart, insn, categoriesForIndex, - curMovesRequired); +outMovesRequired.set(i); + } else { + fitWidth = -1; + break; + } - if (fitWidth < 0) { - continue; - } + seen.set(ssaReg); + } + return fitWidth; + } + + /** + * Converts a bit set of SSA registers into a RegisterSpecList containing + * the definition specs of all the registers. + * + * @param ssaSet {@code non-null;} set of SSA registers + * @return list of RegisterSpecs as noted above + */ + RegisterSpecList ssaSetToSpecs(IntSet ssaSet) { + RegisterSpecList result = new RegisterSpecList(ssaSet.elements()); + + IntIterator iter = ssaSet.iterator(); + + int i = 0; + while (iter.hasNext()) { + result.set(i++, getDefinitionSpecForSsaReg(iter.next())); + } - int score = fitWidth - curMovesRequired.cardinality(); + return result; + } + + /** + * Gets a local item associated with an ssa register, if one exists. + * + * @param ssaReg {@code >= 0;} SSA register + * @return {@code null-ok;} associated local item or null + */ + private LocalItem getLocalItemForReg(int ssaReg) { + for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry : localVariables.entrySet()) { + for (RegisterSpec spec : entry.getValue()) { + if (spec.getReg() == ssaReg) { + return entry.getKey(); + } + } + } - if (score > maxScore) { - maxScore = score; - resultRangeStart = rangeStart; - resultMovesRequired = curMovesRequired; - } + return null; + } - if (fitWidth == rangeLength) { - // We can't do any better than this, so stop here - break; - } - } + /** + * Attempts to map the sources and result of a phi to a common register. + * Will try existing mappings first, from most to least common. If none + * of the registers have mappings yet, a new mapping is created. + */ + private void processPhiInsn(PhiInsn insn) { + RegisterSpec result = insn.getResult(); + int resultReg = result.getReg(); + int category = result.getCategory(); - /* - * If we were unable to find a plan for a fit centered around - * an already-mapped source, just try to find a range of - * registers we can move the range into. - */ + RegisterSpecList sources = insn.getSources(); + int sourcesSize = sources.size(); - if (resultRangeStart == -1) { - resultMovesRequired = new BitSet(szSources); + // List of phi sources / result that need mapping + ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>(); - resultRangeStart = findAnyFittingRange(insn, rangeLength, - categoriesForIndex, resultMovesRequired); - } + // Track how many times a particular mapping is found + Multiset mapSet = new Multiset(sourcesSize + 1); - /* - * Now, insert any moves required. - */ + /* + * If the result of the phi has an existing mapping, get it. + * Otherwise, add it to the list of regs that need mapping. + */ + if (ssaRegsMapped.get(resultReg)) { + mapSet.add(mapper.oldToNew(resultReg)); + } else { + ssaRegs.add(result); + } - for (int i = resultMovesRequired.nextSetBit(0); i >= 0; - i = resultMovesRequired.nextSetBit(i+1)) { - insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i))); - } + for (int i = 0; i < sourcesSize; i++) { + RegisterSpec source = sources.get(i); + SsaInsn def = ssaMeth.getDefinitionForRegister(source.getReg()); + RegisterSpec sourceDef = def.getResult(); + int sourceReg = sourceDef.getReg(); + + /* + * If a source of the phi has an existing mapping, get it. + * Otherwise, add it to the list of regs that need mapping. + */ + if (ssaRegsMapped.get(sourceReg)) { + mapSet.add(mapper.oldToNew(sourceReg)); + } else { + ssaRegs.add(sourceDef); + } + } - return resultRangeStart; + // Try all existing mappings, with the most common ones first + for (int i = 0; i < mapSet.getSize(); i++) { + int maxReg = mapSet.getAndRemoveHighestCount(); + tryMapRegs(ssaRegs, maxReg, category, false); } - /** - * Finds an unreserved range that will fit the sources of the - * specified instruction. Does not bother trying to center the range - * around an already-mapped source register; - * - * @param insn {@code non-null;} insn to build range for - * @param rangeLength {@code >=0;} length required in register units - * @param categoriesForIndex {@code non-null;} indexed by source index; - * the category for each source - * @param outMovesRequired {@code non-null;} an output parameter indexed by - * source index that will contain the set of sources which need - * moves inserted - * @return the rop register that starts the fitting range - */ - private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength, - int[] categoriesForIndex, BitSet outMovesRequired) { - int rangeStart = paramRangeEnd; - while (true) { - rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength); - int fitWidth - = fitPlanForRange(rangeStart, insn, - categoriesForIndex, outMovesRequired); - - if (fitWidth >= 0) { - break; - } - rangeStart++; - outMovesRequired.clear(); - } - return rangeStart; + // Map any remaining unmapped regs with whatever fits + int mapReg = findNextUnreservedRopReg(paramRangeEnd, category); + while (!tryMapRegs(ssaRegs, mapReg, category, false)) { + mapReg = findNextUnreservedRopReg(mapReg + 1, category); } + } + + // A set that tracks how often elements are added to it. + private static class Multiset { + private final int[] reg; + private final int[] count; + private int size; /** - * Attempts to build a plan for fitting a range of sources into rop - * registers. + * Constructs an instance. * - * @param ropReg {@code >= 0;} rop reg that begins range - * @param insn {@code non-null;} insn to plan range for - * @param categoriesForIndex {@code non-null;} indexed by source index; - * the category for each source - * @param outMovesRequired {@code non-null;} an output parameter indexed by - * source index that will contain the set of sources which need - * moves inserted - * @return the width of the fit that that does not involve added moves or - * {@code -1} if "no fit possible" + * @param maxSize the maximum distinct elements the set may have */ - private int fitPlanForRange(int ropReg, NormalSsaInsn insn, - int[] categoriesForIndex, BitSet outMovesRequired) { - RegisterSpecList sources = insn.getSources(); - int szSources = sources.size(); - int fitWidth = 0; - IntSet liveOut = insn.getBlock().getLiveOutRegs(); - RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut); - - // An SSA reg may only be mapped into a range once. - BitSet seen = new BitSet(ssaMeth.getRegCount()); - - for (int i = 0; i < szSources ; i++) { - RegisterSpec ssaSpec = sources.get(i); - int ssaReg = ssaSpec.getReg(); - int category = categoriesForIndex[i]; - - if (i != 0) { - ropReg += categoriesForIndex[i-1]; - } - - if (ssaRegsMapped.get(ssaReg) - && mapper.oldToNew(ssaReg) == ropReg) { - // This is a register that is already mapped appropriately. - fitWidth += category; - } else if (rangeContainsReserved(ropReg, category)) { - fitWidth = -1; - break; - } else if (!ssaRegsMapped.get(ssaReg) - && canMapReg(ssaSpec, ropReg) - && !seen.get(ssaReg)) { - // This is a register that can be mapped appropriately. - fitWidth += category; - } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category) - && !mapper.areAnyPinned(sources, ropReg, category)) { - /* - * This is a source that can be moved. We can insert a - * move as long as: - * - * * no SSA register pinned to the desired rop reg - * is live out on the block - * - * * no SSA register pinned to desired rop reg is - * a source of this insn (since this may require - * overlapping moves, which we can't presently handle) - */ - - outMovesRequired.set(i); - } else { - fitWidth = -1; - break; - } - - seen.set(ssaReg); - } - return fitWidth; + public Multiset(int maxSize) { + reg = new int[maxSize]; + count = new int[maxSize]; + size = 0; } /** - * Converts a bit set of SSA registers into a RegisterSpecList containing - * the definition specs of all the registers. + * Adds an element to the set. * - * @param ssaSet {@code non-null;} set of SSA registers - * @return list of RegisterSpecs as noted above + * @param element element to add */ - RegisterSpecList ssaSetToSpecs(IntSet ssaSet) { - RegisterSpecList result = new RegisterSpecList(ssaSet.elements()); - - IntIterator iter = ssaSet.iterator(); - - int i = 0; - while (iter.hasNext()) { - result.set(i++, getDefinitionSpecForSsaReg(iter.next())); + public void add(int element) { + for (int i = 0; i < size; i++) { + if (reg[i] == element) { + count[i]++; + return; } + } - return result; + reg[size] = element; + count[size] = 1; + size++; } /** - * Gets a local item associated with an ssa register, if one exists. + * Searches the set for the element that has been added the most. + * In the case of a tie, the element that was added first is returned. + * Then, it clears the count on that element. The size of the set + * remains unchanged. * - * @param ssaReg {@code >= 0;} SSA register - * @return {@code null-ok;} associated local item or null + * @return element with the highest count */ - private LocalItem getLocalItemForReg(int ssaReg) { - for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry : - localVariables.entrySet()) { - for (RegisterSpec spec : entry.getValue()) { - if (spec.getReg() == ssaReg) { - return entry.getKey(); - } - } + public int getAndRemoveHighestCount() { + int maxIndex = -1; + int maxReg = -1; + int maxCount = 0; + + for (int i = 0; i < size; i++) { + if (maxCount < count[i]) { + maxIndex = i; + maxReg = reg[i]; + maxCount = count[i]; } + } - return null; + count[maxIndex] = 0; + return maxReg; } /** - * Attempts to map the sources and result of a phi to a common register. - * Will try existing mappings first, from most to least common. If none - * of the registers have mappings yet, a new mapping is created. + * Gets the number of distinct elements in the set. + * + * @return size of the set */ - private void processPhiInsn(PhiInsn insn) { - RegisterSpec result = insn.getResult(); - int resultReg = result.getReg(); - int category = result.getCategory(); - - RegisterSpecList sources = insn.getSources(); - int sourcesSize = sources.size(); - - // List of phi sources / result that need mapping - ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>(); - - // Track how many times a particular mapping is found - Multiset mapSet = new Multiset(sourcesSize + 1); - - /* - * If the result of the phi has an existing mapping, get it. - * Otherwise, add it to the list of regs that need mapping. - */ - if (ssaRegsMapped.get(resultReg)) { - mapSet.add(mapper.oldToNew(resultReg)); - } else { - ssaRegs.add(result); - } - - for (int i = 0; i < sourcesSize; i++) { - RegisterSpec source = sources.get(i); - SsaInsn def = ssaMeth.getDefinitionForRegister(source.getReg()); - RegisterSpec sourceDef = def.getResult(); - int sourceReg = sourceDef.getReg(); - - /* - * If a source of the phi has an existing mapping, get it. - * Otherwise, add it to the list of regs that need mapping. - */ - if (ssaRegsMapped.get(sourceReg)) { - mapSet.add(mapper.oldToNew(sourceReg)); - } else { - ssaRegs.add(sourceDef); - } - } - - // Try all existing mappings, with the most common ones first - for (int i = 0; i < mapSet.getSize(); i++) { - int maxReg = mapSet.getAndRemoveHighestCount(); - tryMapRegs(ssaRegs, maxReg, category, false); - } - - // Map any remaining unmapped regs with whatever fits - int mapReg = findNextUnreservedRopReg(paramRangeEnd, category); - while (!tryMapRegs(ssaRegs, mapReg, category, false)) { - mapReg = findNextUnreservedRopReg(mapReg + 1, category); - } - } - - // A set that tracks how often elements are added to it. - private static class Multiset { - private final int[] reg; - private final int[] count; - private int size; - - /** - * Constructs an instance. - * - * @param maxSize the maximum distinct elements the set may have - */ - public Multiset(int maxSize) { - reg = new int[maxSize]; - count = new int[maxSize]; - size = 0; - } - - /** - * Adds an element to the set. - * - * @param element element to add - */ - public void add(int element) { - for (int i = 0; i < size; i++) { - if (reg[i] == element) { - count[i]++; - return; - } - } - - reg[size] = element; - count[size] = 1; - size++; - } - - /** - * Searches the set for the element that has been added the most. - * In the case of a tie, the element that was added first is returned. - * Then, it clears the count on that element. The size of the set - * remains unchanged. - * - * @return element with the highest count - */ - public int getAndRemoveHighestCount() { - int maxIndex = -1; - int maxReg = -1; - int maxCount = 0; - - for (int i = 0; i < size; i++) { - if (maxCount < count[i]) { - maxIndex = i; - maxReg = reg[i]; - maxCount = count[i]; - } - } - - count[maxIndex] = 0; - return maxReg; - } - - /** - * Gets the number of distinct elements in the set. - * - * @return size of the set - */ - public int getSize() { - return size; - } + public int getSize() { + return size; } + } } diff --git a/dx/src/com/android/jack/dx/ssa/back/IdenticalBlockCombiner.java b/dx/src/com/android/jack/dx/ssa/back/IdenticalBlockCombiner.java index c679d61..9e8b870 100644 --- a/dx/src/com/android/jack/dx/ssa/back/IdenticalBlockCombiner.java +++ b/dx/src/com/android/jack/dx/ssa/back/IdenticalBlockCombiner.java @@ -18,12 +18,8 @@ package com.android.jack.dx.ssa.back; import com.android.jack.dx.rop.code.BasicBlock; import com.android.jack.dx.rop.code.BasicBlockList; -import com.android.jack.dx.rop.code.CstInsn; -import com.android.jack.dx.rop.code.Insn; -import com.android.jack.dx.rop.code.InsnList; import com.android.jack.dx.rop.code.RegOps; import com.android.jack.dx.rop.code.RopMethod; -import com.android.jack.dx.rop.code.SwitchInsn; import com.android.jack.dx.util.IntList; import java.util.BitSet; @@ -35,148 +31,145 @@ import java.util.BitSet; * frequently are created when catch blocks are edge-split. */ public class IdenticalBlockCombiner { - private final RopMethod ropMethod; - private final BasicBlockList blocks; - private final BasicBlockList newBlocks; - - /** - * Constructs instance. Call {@code process()} to run. - * - * @param rm {@code non-null;} instance to process - */ - public IdenticalBlockCombiner(RopMethod rm) { - ropMethod = rm; - blocks = ropMethod.getBlocks(); - newBlocks = blocks.getMutableCopy(); - } - - /** - * Runs algorithm. TODO: This is n^2, and could be made linear-ish with - * a hash. In particular, hash the contents of each block and only - * compare blocks with the same hash. - * - * @return {@code non-null;} new method that has been processed - */ - public RopMethod process() { - int szBlocks = blocks.size(); - // indexed by label - BitSet toDelete = new BitSet(blocks.getMaxLabel()); - - // For each non-deleted block... - for (int bindex = 0; bindex < szBlocks; bindex++) { - BasicBlock b = blocks.get(bindex); - - if (toDelete.get(b.getLabel())) { - // doomed block - continue; - } - - IntList preds = ropMethod.labelToPredecessors(b.getLabel()); - - // ...look at all of it's predecessors that have only one succ... - int szPreds = preds.size(); - for (int i = 0; i < szPreds; i++) { - int iLabel = preds.get(i); - - BasicBlock iBlock = blocks.labelToBlock(iLabel); - - if (toDelete.get(iLabel) - || iBlock.getSuccessors().size() > 1 - || iBlock.getFirstInsn().getOpcode().getOpcode() == - RegOps.MOVE_RESULT) { - continue; - } - - IntList toCombine = new IntList(); - - // ...and see if they can be combined with any other preds... - for (int j = i + 1; j < szPreds; j++) { - int jLabel = preds.get(j); - BasicBlock jBlock = blocks.labelToBlock(jLabel); - - if (jBlock.getSuccessors().size() == 1 - && compareInsns(iBlock, jBlock)) { - - toCombine.add(jLabel); - toDelete.set(jLabel); - } - } - - combineBlocks(iLabel, toCombine); - } + private final RopMethod ropMethod; + private final BasicBlockList blocks; + private final BasicBlockList newBlocks; + + /** + * Constructs instance. Call {@code process()} to run. + * + * @param rm {@code non-null;} instance to process + */ + public IdenticalBlockCombiner(RopMethod rm) { + ropMethod = rm; + blocks = ropMethod.getBlocks(); + newBlocks = blocks.getMutableCopy(); + } + + /** + * Runs algorithm. TODO(dx team): This is n^2, and could be made linear-ish with + * a hash. In particular, hash the contents of each block and only + * compare blocks with the same hash. + * + * @return {@code non-null;} new method that has been processed + */ + public RopMethod process() { + int szBlocks = blocks.size(); + // indexed by label + BitSet toDelete = new BitSet(blocks.getMaxLabel()); + + // For each non-deleted block... + for (int bindex = 0; bindex < szBlocks; bindex++) { + BasicBlock b = blocks.get(bindex); + + if (toDelete.get(b.getLabel())) { + // doomed block + continue; + } + + IntList preds = ropMethod.labelToPredecessors(b.getLabel()); + + // ...look at all of it's predecessors that have only one succ... + int szPreds = preds.size(); + for (int i = 0; i < szPreds; i++) { + int iLabel = preds.get(i); + + BasicBlock iBlock = blocks.labelToBlock(iLabel); + + if (toDelete.get(iLabel) || iBlock.getSuccessors().size() > 1 + || iBlock.getFirstInsn().getOpcode().getOpcode() == RegOps.MOVE_RESULT) { + continue; } - for (int i = szBlocks - 1; i >= 0; i--) { - if (toDelete.get(newBlocks.get(i).getLabel())) { - newBlocks.set(i, null); - } - } + IntList toCombine = new IntList(); - newBlocks.shrinkToFit(); - newBlocks.setImmutable(); + // ...and see if they can be combined with any other preds... + for (int j = i + 1; j < szPreds; j++) { + int jLabel = preds.get(j); + BasicBlock jBlock = blocks.labelToBlock(jLabel); - return new RopMethod(newBlocks, ropMethod.getFirstLabel()); - } + if (jBlock.getSuccessors().size() == 1 && compareInsns(iBlock, jBlock)) { + + toCombine.add(jLabel); + toDelete.set(jLabel); + } + } - /** - * Helper method to compare the contents of two blocks. - * - * @param a {@code non-null;} a block to compare - * @param b {@code non-null;} another block to compare - * @return {@code true} iff the two blocks' instructions are the same - */ - private static boolean compareInsns(BasicBlock a, BasicBlock b) { - return a.getInsns().contentEquals(b.getInsns()); + combineBlocks(iLabel, toCombine); + } } - /** - * Combines blocks proven identical into one alpha block, re-writing - * all of the successor links that point to the beta blocks to point - * to the alpha block instead. - * - * @param alphaLabel block that will replace all the beta block - * @param betaLabels label list of blocks to combine - */ - private void combineBlocks(int alphaLabel, IntList betaLabels) { - int szBetas = betaLabels.size(); - - for (int i = 0; i < szBetas; i++) { - int betaLabel = betaLabels.get(i); - BasicBlock bb = blocks.labelToBlock(betaLabel); - IntList preds = ropMethod.labelToPredecessors(bb.getLabel()); - int szPreds = preds.size(); - - for (int j = 0; j < szPreds; j++) { - BasicBlock predBlock = newBlocks.labelToBlock(preds.get(j)); - replaceSucc(predBlock, betaLabel, alphaLabel); - } - } + for (int i = szBlocks - 1; i >= 0; i--) { + if (toDelete.get(newBlocks.get(i).getLabel())) { + newBlocks.set(i, null); + } } - /** - * Replaces one of a block's successors with a different label. Constructs - * an updated BasicBlock instance and places it in {@code newBlocks}. - * - * @param block block to replace - * @param oldLabel label of successor to replace - * @param newLabel label of new successor - */ - private void replaceSucc(BasicBlock block, int oldLabel, int newLabel) { - IntList newSuccessors = block.getSuccessors().mutableCopy(); - int newPrimarySuccessor; - - newSuccessors.set(newSuccessors.indexOf(oldLabel), newLabel); - newPrimarySuccessor = block.getPrimarySuccessor(); - - if (newPrimarySuccessor == oldLabel) { - newPrimarySuccessor = newLabel; - } + newBlocks.shrinkToFit(); + newBlocks.setImmutable(); + + return new RopMethod(newBlocks, ropMethod.getFirstLabel()); + } + + /** + * Helper method to compare the contents of two blocks. + * + * @param a {@code non-null;} a block to compare + * @param b {@code non-null;} another block to compare + * @return {@code true} iff the two blocks' instructions are the same + */ + private static boolean compareInsns(BasicBlock a, BasicBlock b) { + return a.getInsns().contentEquals(b.getInsns()); + } + + /** + * Combines blocks proven identical into one alpha block, re-writing + * all of the successor links that point to the beta blocks to point + * to the alpha block instead. + * + * @param alphaLabel block that will replace all the beta block + * @param betaLabels label list of blocks to combine + */ + private void combineBlocks(int alphaLabel, IntList betaLabels) { + int szBetas = betaLabels.size(); + + for (int i = 0; i < szBetas; i++) { + int betaLabel = betaLabels.get(i); + BasicBlock bb = blocks.labelToBlock(betaLabel); + IntList preds = ropMethod.labelToPredecessors(bb.getLabel()); + int szPreds = preds.size(); + + for (int j = 0; j < szPreds; j++) { + BasicBlock predBlock = newBlocks.labelToBlock(preds.get(j)); + replaceSucc(predBlock, betaLabel, alphaLabel); + } + } + } + + /** + * Replaces one of a block's successors with a different label. Constructs + * an updated BasicBlock instance and places it in {@code newBlocks}. + * + * @param block block to replace + * @param oldLabel label of successor to replace + * @param newLabel label of new successor + */ + private void replaceSucc(BasicBlock block, int oldLabel, int newLabel) { + IntList newSuccessors = block.getSuccessors().mutableCopy(); + int newPrimarySuccessor; + + newSuccessors.set(newSuccessors.indexOf(oldLabel), newLabel); + newPrimarySuccessor = block.getPrimarySuccessor(); + + if (newPrimarySuccessor == oldLabel) { + newPrimarySuccessor = newLabel; + } - newSuccessors.setImmutable(); + newSuccessors.setImmutable(); - BasicBlock newBB = new BasicBlock(block.getLabel(), - block.getInsns(), newSuccessors, newPrimarySuccessor); + BasicBlock newBB = + new BasicBlock(block.getLabel(), block.getInsns(), newSuccessors, newPrimarySuccessor); - newBlocks.set(newBlocks.indexOfLabel(block.getLabel()), newBB); - } + newBlocks.set(newBlocks.indexOfLabel(block.getLabel()), newBB); + } } diff --git a/dx/src/com/android/jack/dx/ssa/back/InterferenceGraph.java b/dx/src/com/android/jack/dx/ssa/back/InterferenceGraph.java index 00a2c69..a93a2a7 100644 --- a/dx/src/com/android/jack/dx/ssa/back/InterferenceGraph.java +++ b/dx/src/com/android/jack/dx/ssa/back/InterferenceGraph.java @@ -16,98 +16,89 @@ package com.android.jack.dx.ssa.back; -import com.android.jack.dx.rop.code.RegisterSpec; -import com.android.jack.dx.ssa.PhiInsn; import com.android.jack.dx.ssa.SetFactory; -import com.android.jack.dx.ssa.SsaBasicBlock; -import com.android.jack.dx.ssa.SsaInsn; -import com.android.jack.dx.ssa.SsaMethod; -import com.android.jack.dx.util.BitIntSet; import com.android.jack.dx.util.IntSet; -import com.android.jack.dx.util.ListIntSet; -import java.util.BitSet; -import java.util.List; import java.util.ArrayList; /** * A register interference graph */ public class InterferenceGraph { - /** - * {@code non-null;} interference graph, indexed by register in - * both dimensions - */ - private final ArrayList<IntSet> interference; - - /** - * Creates a new graph. - * - * @param countRegs {@code >= 0;} the start count of registers in - * the namespace. New registers can be added subsequently. - */ - public InterferenceGraph(int countRegs) { - interference = new ArrayList<IntSet>(countRegs); - - for (int i = 0; i < countRegs; i++) { - interference.add(SetFactory.makeInterferenceSet(countRegs)); - } + /** + * {@code non-null;} interference graph, indexed by register in + * both dimensions + */ + private final ArrayList<IntSet> interference; + + /** + * Creates a new graph. + * + * @param countRegs {@code >= 0;} the start count of registers in + * the namespace. New registers can be added subsequently. + */ + public InterferenceGraph(int countRegs) { + interference = new ArrayList<IntSet>(countRegs); + + for (int i = 0; i < countRegs; i++) { + interference.add(SetFactory.makeInterferenceSet(countRegs)); } - - /** - * Adds a register pair to the interference/liveness graph. Parameter - * order is insignificant. - * - * @param regV one register index - * @param regW another register index - */ - public void add(int regV, int regW) { - ensureCapacity(Math.max(regV, regW) + 1); - - interference.get(regV).add(regW); - interference.get(regW).add(regV); + } + + /** + * Adds a register pair to the interference/liveness graph. Parameter + * order is insignificant. + * + * @param regV one register index + * @param regW another register index + */ + public void add(int regV, int regW) { + ensureCapacity(Math.max(regV, regW) + 1); + + interference.get(regV).add(regW); + interference.get(regW).add(regV); + } + + /** + * Dumps interference graph to stdout for debugging. + */ + public void dumpToStdout() { + int oldRegCount = interference.size(); + + for (int i = 0; i < oldRegCount; i++) { + StringBuilder sb = new StringBuilder(); + + sb.append("Reg " + i + ":" + interference.get(i).toString()); + + System.out.println(sb.toString()); } - - /** - * Dumps interference graph to stdout for debugging. - */ - public void dumpToStdout() { - int oldRegCount = interference.size(); - - for (int i = 0; i < oldRegCount; i++) { - StringBuilder sb = new StringBuilder(); - - sb.append("Reg " + i + ":" + interference.get(i).toString()); - - System.out.println(sb.toString()); - } - } - - /** - * Merges the interference set for a register into a given bit set - * - * @param reg {@code >= 0;} register - * @param set {@code non-null;} interference set; will be merged - * with set for given register - */ - public void mergeInterferenceSet(int reg, IntSet set) { - if (reg < interference.size()) { - set.merge(interference.get(reg)); - } + } + + /** + * Merges the interference set for a register into a given bit set + * + * @param reg {@code >= 0;} register + * @param set {@code non-null;} interference set; will be merged + * with set for given register + */ + public void mergeInterferenceSet(int reg, IntSet set) { + if (reg < interference.size()) { + set.merge(interference.get(reg)); } + } - /** - * Ensures that the interference graph is appropriately sized. - * - * @param size requested minumum size - */ - private void ensureCapacity(int size) { - int countRegs = interference.size(); + /** + * Ensures that the interference graph is appropriately sized. + * + * @param size requested minumum size + */ + private void ensureCapacity(int size) { + int countRegs = interference.size(); - interference.ensureCapacity(size); + interference.ensureCapacity(size); - for (int i = countRegs; i < size; i++) { - interference.add(SetFactory.makeInterferenceSet(size)); - } + for (int i = countRegs; i < size; i++) { + interference.add(SetFactory.makeInterferenceSet(size)); } + } } diff --git a/dx/src/com/android/jack/dx/ssa/back/LivenessAnalyzer.java b/dx/src/com/android/jack/dx/ssa/back/LivenessAnalyzer.java index 44c9964..5c37f07 100644 --- a/dx/src/com/android/jack/dx/ssa/back/LivenessAnalyzer.java +++ b/dx/src/com/android/jack/dx/ssa/back/LivenessAnalyzer.java @@ -22,9 +22,9 @@ import com.android.jack.dx.ssa.SsaBasicBlock; import com.android.jack.dx.ssa.SsaInsn; import com.android.jack.dx.ssa.SsaMethod; +import java.util.ArrayList; import java.util.BitSet; import java.util.List; -import java.util.ArrayList; /** * From Appel "Modern Compiler Implementation in Java" algorithm 19.17 @@ -35,253 +35,244 @@ import java.util.ArrayList; * M = visitedBlocks <p> */ public class LivenessAnalyzer { - /** - * {@code non-null;} index by basic block indexed set of basic blocks - * that have already been visited. "M" as written in the original Appel - * algorithm. - */ - private final BitSet visitedBlocks; - - /** - * {@code non-null;} set of blocks remaing to visit as "live out as block" - */ - private final BitSet liveOutBlocks; - - /** - * {@code >=0;} SSA register currently being analyzed. - * "v" in the original Appel algorithm. - */ - private final int regV; - - /** method to process */ - private final SsaMethod ssaMeth; - - /** interference graph being updated */ - private final InterferenceGraph interference; - - /** block "n" in Appel 19.17 */ - private SsaBasicBlock blockN; - - /** index of statement {@code s} in {@code blockN} */ - private int statementIndex; - - /** the next function to call */ - private NextFunction nextFunction; - - /** constants for {@link #nextFunction} */ - private static enum NextFunction { - LIVE_IN_AT_STATEMENT, - LIVE_OUT_AT_STATEMENT, - LIVE_OUT_AT_BLOCK, - DONE; + /** + * {@code non-null;} index by basic block indexed set of basic blocks + * that have already been visited. "M" as written in the original Appel + * algorithm. + */ + private final BitSet visitedBlocks; + + /** + * {@code non-null;} set of blocks remaing to visit as "live out as block" + */ + private final BitSet liveOutBlocks; + + /** + * {@code >=0;} SSA register currently being analyzed. + * "v" in the original Appel algorithm. + */ + private final int regV; + + /** method to process */ + private final SsaMethod ssaMeth; + + /** interference graph being updated */ + private final InterferenceGraph interference; + + /** block "n" in Appel 19.17 */ + private SsaBasicBlock blockN; + + /** index of statement {@code s} in {@code blockN} */ + private int statementIndex; + + /** the next function to call */ + private NextFunction nextFunction; + + /** constants for {@link #nextFunction} */ + private static enum NextFunction { + LIVE_IN_AT_STATEMENT, LIVE_OUT_AT_STATEMENT, LIVE_OUT_AT_BLOCK, DONE; + } + + /** + * Runs register liveness algorithm for a method, updating the + * live in/out information in {@code SsaBasicBlock} instances and + * returning an interference graph. + * + * @param ssaMeth {@code non-null;} method to process + * @return {@code non-null;} interference graph indexed by SSA + * registers in both directions + */ + public static InterferenceGraph constructInterferenceGraph(SsaMethod ssaMeth) { + int szRegs = ssaMeth.getRegCount(); + InterferenceGraph interference = new InterferenceGraph(szRegs); + + for (int i = 0; i < szRegs; i++) { + new LivenessAnalyzer(ssaMeth, i, interference).run(); } - /** - * Runs register liveness algorithm for a method, updating the - * live in/out information in {@code SsaBasicBlock} instances and - * returning an interference graph. - * - * @param ssaMeth {@code non-null;} method to process - * @return {@code non-null;} interference graph indexed by SSA - * registers in both directions - */ - public static InterferenceGraph constructInterferenceGraph( - SsaMethod ssaMeth) { - int szRegs = ssaMeth.getRegCount(); - InterferenceGraph interference = new InterferenceGraph(szRegs); - - for (int i = 0; i < szRegs; i++) { - new LivenessAnalyzer(ssaMeth, i, interference).run(); - } + coInterferePhis(ssaMeth, interference); + + return interference; + } + + /** + * Makes liveness analyzer instance for specific register. + * + * @param ssaMeth {@code non-null;} method to process + * @param reg register whose liveness to analyze + * @param interference {@code non-null;} indexed by SSA reg in + * both dimensions; graph to update + * + */ + private LivenessAnalyzer(SsaMethod ssaMeth, int reg, InterferenceGraph interference) { + int blocksSz = ssaMeth.getBlocks().size(); + + this.ssaMeth = ssaMeth; + this.regV = reg; + visitedBlocks = new BitSet(blocksSz); + liveOutBlocks = new BitSet(blocksSz); + this.interference = interference; + } + + /** + * The algorithm in Appel is presented in partial tail-recursion + * form. Obviously, that's not efficient in java, so this function + * serves as the dispatcher instead. + */ + private void handleTailRecursion() { + while (nextFunction != NextFunction.DONE) { + switch (nextFunction) { + case LIVE_IN_AT_STATEMENT: + nextFunction = NextFunction.DONE; + liveInAtStatement(); + break; + + case LIVE_OUT_AT_STATEMENT: + nextFunction = NextFunction.DONE; + liveOutAtStatement(); + break; + + case LIVE_OUT_AT_BLOCK: + nextFunction = NextFunction.DONE; + liveOutAtBlock(); + break; + + default: + } + } + } - coInterferePhis(ssaMeth, interference); + /** + * From Appel algorithm 19.17. + */ + public void run() { + List<SsaInsn> useList = ssaMeth.getUseListForRegister(regV); - return interference; - } + for (SsaInsn insn : useList) { + nextFunction = NextFunction.DONE; - /** - * Makes liveness analyzer instance for specific register. - * - * @param ssaMeth {@code non-null;} method to process - * @param reg register whose liveness to analyze - * @param interference {@code non-null;} indexed by SSA reg in - * both dimensions; graph to update - * - */ - private LivenessAnalyzer(SsaMethod ssaMeth, int reg, - InterferenceGraph interference) { - int blocksSz = ssaMeth.getBlocks().size(); - - this.ssaMeth = ssaMeth; - this.regV = reg; - visitedBlocks = new BitSet(blocksSz); - liveOutBlocks = new BitSet(blocksSz); - this.interference = interference; - } + if (insn instanceof PhiInsn) { + // If s is a phi-function with V as it's ith argument. + PhiInsn phi = (PhiInsn) insn; - /** - * The algorithm in Appel is presented in partial tail-recursion - * form. Obviously, that's not efficient in java, so this function - * serves as the dispatcher instead. - */ - private void handleTailRecursion() { - while (nextFunction != NextFunction.DONE) { - switch (nextFunction) { - case LIVE_IN_AT_STATEMENT: - nextFunction = NextFunction.DONE; - liveInAtStatement(); - break; - - case LIVE_OUT_AT_STATEMENT: - nextFunction = NextFunction.DONE; - liveOutAtStatement(); - break; - - case LIVE_OUT_AT_BLOCK: - nextFunction = NextFunction.DONE; - liveOutAtBlock(); - break; - - default: - } - } - } + for (SsaBasicBlock pred : phi.predBlocksForReg(regV, ssaMeth)) { + blockN = pred; - /** - * From Appel algorithm 19.17. - */ - public void run() { - List<SsaInsn> useList = ssaMeth.getUseListForRegister(regV); - - for (SsaInsn insn : useList) { - nextFunction = NextFunction.DONE; - - if (insn instanceof PhiInsn) { - // If s is a phi-function with V as it's ith argument. - PhiInsn phi = (PhiInsn) insn; - - for (SsaBasicBlock pred : - phi.predBlocksForReg(regV, ssaMeth)) { - blockN = pred; - - nextFunction = NextFunction.LIVE_OUT_AT_BLOCK; - handleTailRecursion(); - } - } else { - // Special management for registers of category 2, indeed they use - // two 32-bits registers thus there is an implicit interference between - // the result register and operands contrary to category 1 where there - // is no writing of registers before all operands are read. - RegisterSpec resultSpec = insn.getResult(); - if (resultSpec != null && resultSpec.getCategory() == 2 - && insn.getSources().specForRegister(regV).getCategory() == 2) { - interference.add(regV, resultSpec.getReg()); - } - - blockN = insn.getBlock(); - statementIndex = blockN.getInsns().indexOf(insn); - - if (statementIndex < 0) { - throw new RuntimeException( - "insn not found in it's own block"); - } - - nextFunction = NextFunction.LIVE_IN_AT_STATEMENT; - handleTailRecursion(); - } + nextFunction = NextFunction.LIVE_OUT_AT_BLOCK; + handleTailRecursion(); } + } else { + // Special management for registers of category 2, indeed they use + // two 32-bits registers thus there is an implicit interference between + // the result register and operands contrary to category 1 where there + // is no writing of registers before all operands are read. + RegisterSpec resultSpec = insn.getResult(); + if (resultSpec != null && resultSpec.getCategory() == 2 + && insn.getSources().specForRegister(regV).getCategory() == 2) { + interference.add(regV, resultSpec.getReg()); + } + + blockN = insn.getBlock(); + statementIndex = blockN.getInsns().indexOf(insn); - int nextLiveOutBlock; - while ((nextLiveOutBlock = liveOutBlocks.nextSetBit(0)) >= 0) { - blockN = ssaMeth.getBlocks().get(nextLiveOutBlock); - liveOutBlocks.clear(nextLiveOutBlock); - nextFunction = NextFunction.LIVE_OUT_AT_BLOCK; - handleTailRecursion(); + if (statementIndex < 0) { + throw new RuntimeException("insn not found in it's own block"); } + + nextFunction = NextFunction.LIVE_IN_AT_STATEMENT; + handleTailRecursion(); + } } - /** - * "v is live-out at n." - */ - private void liveOutAtBlock() { - if (! visitedBlocks.get(blockN.getIndex())) { - visitedBlocks.set(blockN.getIndex()); + int nextLiveOutBlock; + while ((nextLiveOutBlock = liveOutBlocks.nextSetBit(0)) >= 0) { + blockN = ssaMeth.getBlocks().get(nextLiveOutBlock); + liveOutBlocks.clear(nextLiveOutBlock); + nextFunction = NextFunction.LIVE_OUT_AT_BLOCK; + handleTailRecursion(); + } + } - blockN.addLiveOut(regV); + /** + * "v is live-out at n." + */ + private void liveOutAtBlock() { + if (!visitedBlocks.get(blockN.getIndex())) { + visitedBlocks.set(blockN.getIndex()); - ArrayList<SsaInsn> insns; + blockN.addLiveOut(regV); - insns = blockN.getInsns(); + ArrayList<SsaInsn> insns; - // Live out at last statement in blockN - statementIndex = insns.size() - 1; - nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT; - } - } + insns = blockN.getInsns(); - /** - * "v is live-in at s." - */ - private void liveInAtStatement() { - // if s is the first statement in block N - if (statementIndex == 0) { - // v is live-in at n - blockN.addLiveIn(regV); - - BitSet preds = blockN.getPredecessors(); - - liveOutBlocks.or(preds); - } else { - // Let s' be the statement preceeding s - statementIndex -= 1; - nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT; - } + // Live out at last statement in blockN + statementIndex = insns.size() - 1; + nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT; } - - /** - * "v is live-out at s." - */ - private void liveOutAtStatement() { - SsaInsn statement = blockN.getInsns().get(statementIndex); - RegisterSpec rs = statement.getResult(); - - if (!statement.isResultReg(regV)) { - if (rs != null) { - interference.add(regV, rs.getReg()); - } - nextFunction = NextFunction.LIVE_IN_AT_STATEMENT; - } + } + + /** + * "v is live-in at s." + */ + private void liveInAtStatement() { + // if s is the first statement in block N + if (statementIndex == 0) { + // v is live-in at n + blockN.addLiveIn(regV); + + BitSet preds = blockN.getPredecessors(); + + liveOutBlocks.or(preds); + } else { + // Let s' be the statement preceeding s + statementIndex -= 1; + nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT; } - - /** - * Ensures that all the phi result registers for all the phis in the - * same basic block interfere with each other. This is needed since - * the dead code remover has allowed through "dead-end phis" whose - * results are not used except as local assignments. Without this step, - * a the result of a dead-end phi might be assigned the same register - * as the result of another phi, and the phi removal move scheduler may - * generate moves that over-write the live result. - * - * @param ssaMeth {@code non-null;} method to pricess - * @param interference {@code non-null;} interference graph - */ - private static void coInterferePhis(SsaMethod ssaMeth, - InterferenceGraph interference) { - for (SsaBasicBlock b : ssaMeth.getBlocks()) { - List<SsaInsn> phis = b.getPhiInsns(); - - int szPhis = phis.size(); - - for (int i = 0; i < szPhis; i++) { - for (int j = 0; j < szPhis; j++) { - if (i == j) { - continue; - } - - interference.add(phis.get(i).getResult().getReg(), - phis.get(j).getResult().getReg()); - } - } + } + + /** + * "v is live-out at s." + */ + private void liveOutAtStatement() { + SsaInsn statement = blockN.getInsns().get(statementIndex); + RegisterSpec rs = statement.getResult(); + + if (!statement.isResultReg(regV)) { + if (rs != null) { + interference.add(regV, rs.getReg()); + } + nextFunction = NextFunction.LIVE_IN_AT_STATEMENT; + } + } + + /** + * Ensures that all the phi result registers for all the phis in the + * same basic block interfere with each other. This is needed since + * the dead code remover has allowed through "dead-end phis" whose + * results are not used except as local assignments. Without this step, + * a the result of a dead-end phi might be assigned the same register + * as the result of another phi, and the phi removal move scheduler may + * generate moves that over-write the live result. + * + * @param ssaMeth {@code non-null;} method to pricess + * @param interference {@code non-null;} interference graph + */ + private static void coInterferePhis(SsaMethod ssaMeth, InterferenceGraph interference) { + for (SsaBasicBlock b : ssaMeth.getBlocks()) { + List<SsaInsn> phis = b.getPhiInsns(); + + int szPhis = phis.size(); + + for (int i = 0; i < szPhis; i++) { + for (int j = 0; j < szPhis; j++) { + if (i == j) { + continue; + } + + interference.add(phis.get(i).getResult().getReg(), phis.get(j).getResult().getReg()); } + } } + } } diff --git a/dx/src/com/android/jack/dx/ssa/back/NullRegisterAllocator.java b/dx/src/com/android/jack/dx/ssa/back/NullRegisterAllocator.java index 28b2000..051a159 100644 --- a/dx/src/com/android/jack/dx/ssa/back/NullRegisterAllocator.java +++ b/dx/src/com/android/jack/dx/ssa/back/NullRegisterAllocator.java @@ -20,39 +20,35 @@ import com.android.jack.dx.ssa.BasicRegisterMapper; import com.android.jack.dx.ssa.RegisterMapper; import com.android.jack.dx.ssa.SsaMethod; -import java.util.BitSet; -import java.util.ArrayList; - /** * A register allocator that maps SSA register n to Rop register 2*n, * essentially preserving the original mapping and remaining agnostic * about normal or wide categories. Used for debugging. */ public class NullRegisterAllocator extends RegisterAllocator { - /** {@inheritDoc} */ - public NullRegisterAllocator(SsaMethod ssaMeth, - InterferenceGraph interference) { - super(ssaMeth, interference); - } - - /** {@inheritDoc} */ - @Override - public boolean wantsParamsMovedHigh() { - // We're not smart enough for this. - return false; + /** {@inheritDoc} */ + public NullRegisterAllocator(SsaMethod ssaMeth, InterferenceGraph interference) { + super(ssaMeth, interference); + } + + /** {@inheritDoc} */ + @Override + public boolean wantsParamsMovedHigh() { + // We're not smart enough for this. + return false; + } + + /** {@inheritDoc} */ + @Override + public RegisterMapper allocateRegisters() { + int oldRegCount = ssaMeth.getRegCount(); + + BasicRegisterMapper mapper = new BasicRegisterMapper(oldRegCount); + + for (int i = 0; i < oldRegCount; i++) { + mapper.addMapping(i, i * 2, 2); } - /** {@inheritDoc} */ - @Override - public RegisterMapper allocateRegisters() { - int oldRegCount = ssaMeth.getRegCount(); - - BasicRegisterMapper mapper = new BasicRegisterMapper(oldRegCount); - - for (int i = 0; i < oldRegCount; i++) { - mapper.addMapping(i, i*2, 2); - } - - return mapper; - } + return mapper; + } } diff --git a/dx/src/com/android/jack/dx/ssa/back/RegisterAllocator.java b/dx/src/com/android/jack/dx/ssa/back/RegisterAllocator.java index e81a9d6..9a13b8b 100644 --- a/dx/src/com/android/jack/dx/ssa/back/RegisterAllocator.java +++ b/dx/src/com/android/jack/dx/ssa/back/RegisterAllocator.java @@ -30,168 +30,160 @@ import com.android.jack.dx.ssa.SsaMethod; import com.android.jack.dx.util.IntIterator; import com.android.jack.dx.util.IntSet; -import java.util.BitSet; import java.util.ArrayList; /** * Base class of all register allocators. */ public abstract class RegisterAllocator { - /** method being processed */ - protected final SsaMethod ssaMeth; + /** method being processed */ + protected final SsaMethod ssaMeth; + + /** interference graph, indexed by register in both dimensions */ + protected final InterferenceGraph interference; + + /** + * Creates an instance. Call {@code allocateRegisters} to run. + * @param ssaMeth method to process. + * @param interference Interference graph, indexed by register in both + * dimensions. + */ + public RegisterAllocator(SsaMethod ssaMeth, InterferenceGraph interference) { + this.ssaMeth = ssaMeth; + this.interference = interference; + } + + /** + * Indicates whether the method params were allocated at the bottom + * of the namespace, and thus should be moved up to the top of the + * namespace after phi removal. + * + * @return {@code true} if params should be moved from low to high + */ + public abstract boolean wantsParamsMovedHigh(); + + /** + * Runs the algorithm. + * + * @return a register mapper to apply to the {@code SsaMethod} + */ + public abstract RegisterMapper allocateRegisters(); + + /** + * Returns the category (width) of the definition site of the register. + * Returns {@code 1} for undefined registers. + * + * @param reg register + * @return {@code 1..2} + */ + protected final int getCategoryForSsaReg(int reg) { + SsaInsn definition = ssaMeth.getDefinitionForRegister(reg); + + if (definition == null) { + // an undefined reg + return 1; + } else { + return definition.getResult().getCategory(); + } + } + + /** + * Returns the RegisterSpec of the definition of the register. + * + * @param reg {@code >= 0;} SSA register + * @return definition spec of the register or null if it is never defined + * (for the case of "version 0" SSA registers) + */ + protected final RegisterSpec getDefinitionSpecForSsaReg(int reg) { + SsaInsn definition = ssaMeth.getDefinitionForRegister(reg); + + return definition == null ? null : definition.getResult(); + } + + /** + * Returns true if the definition site of this register is a + * move-param (ie, this is a method parameter). + * + * @param reg register in question + * @return {@code true} if this is a method parameter + */ + protected boolean isDefinitionMoveParam(int reg) { + SsaInsn defInsn = ssaMeth.getDefinitionForRegister(reg); + + if (defInsn instanceof NormalSsaInsn) { + NormalSsaInsn ndefInsn = (NormalSsaInsn) defInsn; + + return ndefInsn.getOpcode().getOpcode() == RegOps.MOVE_PARAM; + } - /** interference graph, indexed by register in both dimensions */ - protected final InterferenceGraph interference; + return false; + } + + /** + * Inserts a move instruction for a specified SSA register before a + * specified instruction, creating a new SSA register and adjusting the + * interference graph in the process. The insn currently must be the + * last insn in a block. + * + * @param insn {@code non-null;} insn to insert move before, must + * be last insn in block + * @param reg {@code non-null;} SSA register to duplicate + * @return {@code non-null;} spec of new SSA register created by move + */ + protected final RegisterSpec insertMoveBefore(SsaInsn insn, RegisterSpec reg) { + SsaBasicBlock block = insn.getBlock(); + ArrayList<SsaInsn> insns = block.getInsns(); + int insnIndex = insns.indexOf(insn); + + if (insnIndex < 0) { + throw new IllegalArgumentException("specified insn is not in this block"); + } - /** - * Creates an instance. Call {@code allocateRegisters} to run. - * @param ssaMeth method to process. - * @param interference Interference graph, indexed by register in both - * dimensions. - */ - public RegisterAllocator(SsaMethod ssaMeth, - InterferenceGraph interference) { - this.ssaMeth = ssaMeth; - this.interference = interference; + if (insnIndex != insns.size() - 1) { + /* + * Presently, the interference updater only works when + * adding before the last insn, and the last insn must have no + * result + */ + throw new IllegalArgumentException("Adding move here not supported:" + insn.toHuman()); } - /** - * Indicates whether the method params were allocated at the bottom - * of the namespace, and thus should be moved up to the top of the - * namespace after phi removal. - * - * @return {@code true} if params should be moved from low to high + /* + * Get new register and make new move instruction. */ - public abstract boolean wantsParamsMovedHigh(); - /** - * Runs the algorithm. - * - * @return a register mapper to apply to the {@code SsaMethod} - */ - public abstract RegisterMapper allocateRegisters(); - - /** - * Returns the category (width) of the definition site of the register. - * Returns {@code 1} for undefined registers. - * - * @param reg register - * @return {@code 1..2} - */ - protected final int getCategoryForSsaReg(int reg) { - SsaInsn definition = ssaMeth.getDefinitionForRegister(reg); - - if (definition == null) { - // an undefined reg - return 1; - } else { - return definition.getResult().getCategory(); - } - } +// The new result must not have an associated local variable. + RegisterSpec newRegSpec = RegisterSpec.make(ssaMeth.makeNewSsaReg(), reg.getTypeBearer()); - /** - * Returns the RegisterSpec of the definition of the register. - * - * @param reg {@code >= 0;} SSA register - * @return definition spec of the register or null if it is never defined - * (for the case of "version 0" SSA registers) - */ - protected final RegisterSpec getDefinitionSpecForSsaReg(int reg) { - SsaInsn definition = ssaMeth.getDefinitionForRegister(reg); + SsaInsn toAdd = SsaInsn.makeFromRop(new PlainInsn(Rops.opMove(newRegSpec.getType()), + SourcePosition.NO_INFO, newRegSpec, RegisterSpecList.make(reg)), block); - return definition == null ? null : definition.getResult(); - } + insns.add(insnIndex, toAdd); - /** - * Returns true if the definition site of this register is a - * move-param (ie, this is a method parameter). - * - * @param reg register in question - * @return {@code true} if this is a method parameter - */ - protected boolean isDefinitionMoveParam(int reg) { - SsaInsn defInsn = ssaMeth.getDefinitionForRegister(reg); + int newReg = newRegSpec.getReg(); - if (defInsn instanceof NormalSsaInsn) { - NormalSsaInsn ndefInsn = (NormalSsaInsn) defInsn; + /* + * Adjust interference graph based on what's live out of the current + * block and what's used by the final instruction. + */ - return ndefInsn.getOpcode().getOpcode() == RegOps.MOVE_PARAM; - } +IntSet liveOut = block.getLiveOutRegs(); + IntIterator liveOutIter = liveOut.iterator(); - return false; + while (liveOutIter.hasNext()) { + interference.add(newReg, liveOutIter.next()); } - /** - * Inserts a move instruction for a specified SSA register before a - * specified instruction, creating a new SSA register and adjusting the - * interference graph in the process. The insn currently must be the - * last insn in a block. - * - * @param insn {@code non-null;} insn to insert move before, must - * be last insn in block - * @param reg {@code non-null;} SSA register to duplicate - * @return {@code non-null;} spec of new SSA register created by move - */ - protected final RegisterSpec insertMoveBefore(SsaInsn insn, - RegisterSpec reg) { - SsaBasicBlock block = insn.getBlock(); - ArrayList<SsaInsn> insns = block.getInsns(); - int insnIndex = insns.indexOf(insn); - - if (insnIndex < 0) { - throw new IllegalArgumentException ( - "specified insn is not in this block"); - } - - if (insnIndex != insns.size() - 1) { - /* - * Presently, the interference updater only works when - * adding before the last insn, and the last insn must have no - * result - */ - throw new IllegalArgumentException( - "Adding move here not supported:" + insn.toHuman()); - } - - /* - * Get new register and make new move instruction. - */ - - // The new result must not have an associated local variable. - RegisterSpec newRegSpec = RegisterSpec.make(ssaMeth.makeNewSsaReg(), - reg.getTypeBearer()); - - SsaInsn toAdd = SsaInsn.makeFromRop( - new PlainInsn(Rops.opMove(newRegSpec.getType()), - SourcePosition.NO_INFO, newRegSpec, - RegisterSpecList.make(reg)), block); - - insns.add(insnIndex, toAdd); - - int newReg = newRegSpec.getReg(); - - /* - * Adjust interference graph based on what's live out of the current - * block and what's used by the final instruction. - */ - - IntSet liveOut = block.getLiveOutRegs(); - IntIterator liveOutIter = liveOut.iterator(); - - while (liveOutIter.hasNext()) { - interference.add(newReg, liveOutIter.next()); - } - - // Everything that's a source in the last insn interferes. - RegisterSpecList sources = insn.getSources(); - int szSources = sources.size(); - - for (int i = 0; i < szSources; i++) { - interference.add(newReg, sources.get(i).getReg()); - } - - ssaMeth.onInsnsChanged(); - - return newRegSpec; + // Everything that's a source in the last insn interferes. + RegisterSpecList sources = insn.getSources(); + int szSources = sources.size(); + + for (int i = 0; i < szSources; i++) { + interference.add(newReg, sources.get(i).getReg()); } + + ssaMeth.onInsnsChanged(); + + return newRegSpec; + } } diff --git a/dx/src/com/android/jack/dx/ssa/back/SsaToRop.java b/dx/src/com/android/jack/dx/ssa/back/SsaToRop.java index 2c63a55..9d984f9 100644 --- a/dx/src/com/android/jack/dx/ssa/back/SsaToRop.java +++ b/dx/src/com/android/jack/dx/ssa/back/SsaToRop.java @@ -42,339 +42,326 @@ import java.util.Comparator; * Converts a method in SSA form to ROP form. */ public class SsaToRop { - /** local debug flag */ - private static final boolean DEBUG = false; - - /** {@code non-null;} method to process */ - private final SsaMethod ssaMeth; - - /** - * {@code true} if the converter should attempt to minimize - * the rop-form register count - */ - private final boolean minimizeRegisters; - - /** {@code non-null;} interference graph */ - private final InterferenceGraph interference; - - /** - * Converts a method in SSA form to ROP form. - * - * @param ssaMeth {@code non-null;} method to process - * @param minimizeRegisters {@code true} if the converter should - * attempt to minimize the rop-form register count - * @return {@code non-null;} rop-form output - */ - public static RopMethod convertToRopMethod(SsaMethod ssaMeth, - boolean minimizeRegisters) { - return new SsaToRop(ssaMeth, minimizeRegisters).convert(); - } - - /** - * Constructs an instance. - * - * @param ssaMeth {@code non-null;} method to process - * @param minimizeRegisters {@code true} if the converter should - * attempt to minimize the rop-form register count - */ - private SsaToRop(SsaMethod ssaMethod, boolean minimizeRegisters) { - this.minimizeRegisters = minimizeRegisters; - this.ssaMeth = ssaMethod; - this.interference = - LivenessAnalyzer.constructInterferenceGraph(ssaMethod); + /** local debug flag */ + private static final boolean DEBUG = false; + + /** {@code non-null;} method to process */ + private final SsaMethod ssaMeth; + + /** + * {@code true} if the converter should attempt to minimize + * the rop-form register count + */ + private final boolean minimizeRegisters; + + /** {@code non-null;} interference graph */ + private final InterferenceGraph interference; + + /** + * Converts a method in SSA form to ROP form. + * + * @param ssaMeth {@code non-null;} method to process + * @param minimizeRegisters {@code true} if the converter should + * attempt to minimize the rop-form register count + * @return {@code non-null;} rop-form output + */ + public static RopMethod convertToRopMethod(SsaMethod ssaMeth, boolean minimizeRegisters) { + return new SsaToRop(ssaMeth, minimizeRegisters).convert(); + } + + /** + * Constructs an instance. + * + * @param ssaMeth {@code non-null;} method to process + * @param minimizeRegisters {@code true} if the converter should + * attempt to minimize the rop-form register count + */ + private SsaToRop(SsaMethod ssaMethod, boolean minimizeRegisters) { + this.minimizeRegisters = minimizeRegisters; + this.ssaMeth = ssaMethod; + this.interference = LivenessAnalyzer.constructInterferenceGraph(ssaMethod); + } + + /** + * Performs the conversion. + * + * @return {@code non-null;} rop-form output + */ + private RopMethod convert() { + if (DEBUG) { + interference.dumpToStdout(); } - /** - * Performs the conversion. - * - * @return {@code non-null;} rop-form output - */ - private RopMethod convert() { - if (DEBUG) { - interference.dumpToStdout(); - } - - // These are other allocators for debugging or historical comparison: - // allocator = new NullRegisterAllocator(ssaMeth, interference); - // allocator = new FirstFitAllocator(ssaMeth, interference); - - RegisterAllocator allocator = - new FirstFitLocalCombiningAllocator(ssaMeth, interference, - minimizeRegisters); - - RegisterMapper mapper = allocator.allocateRegisters(); - - if (DEBUG) { - System.out.println("Printing reg map"); - System.out.println(((BasicRegisterMapper)mapper).toHuman()); - } + // These are other allocators for debugging or historical comparison: + // allocator = new NullRegisterAllocator(ssaMeth, interference); + // allocator = new FirstFitAllocator(ssaMeth, interference); - ssaMeth.setBackMode(); + RegisterAllocator allocator = + new FirstFitLocalCombiningAllocator(ssaMeth, interference, minimizeRegisters); - ssaMeth.mapRegisters(mapper); + RegisterMapper mapper = allocator.allocateRegisters(); - removePhiFunctions(); + if (DEBUG) { + System.out.println("Printing reg map"); + System.out.println(((BasicRegisterMapper) mapper).toHuman()); + } - if (allocator.wantsParamsMovedHigh()) { - moveParametersToHighRegisters(); - } + ssaMeth.setBackMode(); - removeEmptyGotos(); + ssaMeth.mapRegisters(mapper); - RopMethod ropMethod = new RopMethod(convertBasicBlocks(), - ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex())); - ropMethod = new IdenticalBlockCombiner(ropMethod).process(); + removePhiFunctions(); - return ropMethod; + if (allocator.wantsParamsMovedHigh()) { + moveParametersToHighRegisters(); } - /** - * Removes all blocks containing only GOTOs from the control flow. - * Although much of this work will be done later when converting - * from rop to dex, not all simplification cases can be handled - * there. Furthermore, any no-op block between the exit block and - * blocks containing the real return or throw statements must be - * removed. - */ - private void removeEmptyGotos() { - final ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); - - ssaMeth.forEachBlockDepthFirst(false, new SsaBasicBlock.Visitor() { - public void visitBlock(SsaBasicBlock b, SsaBasicBlock parent) { - ArrayList<SsaInsn> insns = b.getInsns(); - - if ((insns.size() == 1) - && (insns.get(0).getOpcode() == Rops.GOTO)) { - BitSet preds = (BitSet) b.getPredecessors().clone(); - - for (int i = preds.nextSetBit(0); i >= 0; - i = preds.nextSetBit(i + 1)) { - SsaBasicBlock pb = blocks.get(i); - pb.replaceSuccessor(b.getIndex(), - b.getPrimarySuccessorIndex()); - } - } - } - }); - } - - /** - * See Appel 19.6. To remove the phi instructions in an edge-split - * SSA representation we know we can always insert a move in a - * predecessor block. - */ - private void removePhiFunctions() { - ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); - - for (SsaBasicBlock block : blocks) { - // Add moves in all the pred blocks for each phi insn. - block.forEachPhiInsn(new PhiVisitor(blocks)); + removeEmptyGotos(); + + RopMethod ropMethod = new RopMethod(convertBasicBlocks(), + ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex())); + ropMethod = new IdenticalBlockCombiner(ropMethod).process(); + + return ropMethod; + } + + /** + * Removes all blocks containing only GOTOs from the control flow. + * Although much of this work will be done later when converting + * from rop to dex, not all simplification cases can be handled + * there. Furthermore, any no-op block between the exit block and + * blocks containing the real return or throw statements must be + * removed. + */ + private void removeEmptyGotos() { + final ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); + + ssaMeth.forEachBlockDepthFirst(false, new SsaBasicBlock.Visitor() { + @Override + public void visitBlock(SsaBasicBlock b, SsaBasicBlock parent) { + ArrayList<SsaInsn> insns = b.getInsns(); - // Delete the phi insns. - block.removeAllPhiInsns(); - } + if ((insns.size() == 1) && (insns.get(0).getOpcode() == Rops.GOTO)) { + BitSet preds = (BitSet) b.getPredecessors().clone(); - /* - * After all move insns have been added, sort them so they don't - * destructively interfere. - */ - for (SsaBasicBlock block : blocks) { - block.scheduleMovesFromPhis(); + for (int i = preds.nextSetBit(0); i >= 0; i = preds.nextSetBit(i + 1)) { + SsaBasicBlock pb = blocks.get(i); + pb.replaceSuccessor(b.getIndex(), b.getPrimarySuccessorIndex()); + } } + } + }); + } + + /** + * See Appel 19.6. To remove the phi instructions in an edge-split + * SSA representation we know we can always insert a move in a + * predecessor block. + */ + private void removePhiFunctions() { + ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); + + for (SsaBasicBlock block : blocks) { + // Add moves in all the pred blocks for each phi insn. + block.forEachPhiInsn(new PhiVisitor(blocks)); + + // Delete the phi insns. + block.removeAllPhiInsns(); } - /** - * Helper for {@link #removePhiFunctions}: PhiSuccessorUpdater for - * adding move instructions to predecessors based on phi insns. + /* + * After all move insns have been added, sort them so they don't + * destructively interfere. */ - private static class PhiVisitor implements PhiInsn.Visitor { - private final ArrayList<SsaBasicBlock> blocks; - - public PhiVisitor(ArrayList<SsaBasicBlock> blocks) { - this.blocks = blocks; - } - - public void visitPhiInsn(PhiInsn insn) { - RegisterSpecList sources = insn.getSources(); - RegisterSpec result = insn.getResult(); - int sz = sources.size(); + for (SsaBasicBlock block : blocks) { + block.scheduleMovesFromPhis(); + } + } - for (int i = 0; i < sz; i++) { - RegisterSpec source = sources.get(i); - SsaBasicBlock predBlock = blocks.get( - insn.predBlockIndexForSourcesIndex(i)); + /** + * Helper for {@link #removePhiFunctions}: PhiSuccessorUpdater for + * adding move instructions to predecessors based on phi insns. + */ + private static class PhiVisitor implements PhiInsn.Visitor { + private final ArrayList<SsaBasicBlock> blocks; - predBlock.addMoveToEnd(result, source); - } - } + public PhiVisitor(ArrayList<SsaBasicBlock> blocks) { + this.blocks = blocks; } - /** - * Moves the parameter registers, which allocateRegisters() places - * at the bottom of the frame, up to the top of the frame to match - * Dalvik calling convention. - */ - private void moveParametersToHighRegisters() { - int paramWidth = ssaMeth.getParamWidth(); - BasicRegisterMapper mapper - = new BasicRegisterMapper(ssaMeth.getRegCount()); - int regCount = ssaMeth.getRegCount(); - - for (int i = 0; i < regCount; i++) { - if (i < paramWidth) { - mapper.addMapping(i, regCount - paramWidth + i, 1); - } else { - mapper.addMapping(i, i - paramWidth, 1); - } - } + @Override + public void visitPhiInsn(PhiInsn insn) { + RegisterSpecList sources = insn.getSources(); + RegisterSpec result = insn.getResult(); + int sz = sources.size(); - if (DEBUG) { - System.out.printf("Moving %d registers from 0 to %d\n", - paramWidth, regCount - paramWidth); - } + for (int i = 0; i < sz; i++) { + RegisterSpec source = sources.get(i); + SsaBasicBlock predBlock = blocks.get(insn.predBlockIndexForSourcesIndex(i)); - ssaMeth.mapRegisters(mapper); + predBlock.addMoveToEnd(result, source); + } + } + } + + /** + * Moves the parameter registers, which allocateRegisters() places + * at the bottom of the frame, up to the top of the frame to match + * Dalvik calling convention. + */ + private void moveParametersToHighRegisters() { + int paramWidth = ssaMeth.getParamWidth(); + BasicRegisterMapper mapper = new BasicRegisterMapper(ssaMeth.getRegCount()); + int regCount = ssaMeth.getRegCount(); + + for (int i = 0; i < regCount; i++) { + if (i < paramWidth) { + mapper.addMapping(i, regCount - paramWidth + i, 1); + } else { + mapper.addMapping(i, i - paramWidth, 1); + } } - /** - * @return rop-form basic block list - */ - private BasicBlockList convertBasicBlocks() { - ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); + if (DEBUG) { + System.out.printf("Moving %d registers from 0 to %d\n", paramWidth, regCount - paramWidth); + } - // Exit block may be null. - SsaBasicBlock exitBlock = ssaMeth.getExitBlock(); + ssaMeth.mapRegisters(mapper); + } - ssaMeth.computeReachability(); - int ropBlockCount = ssaMeth.getCountReachableBlocks(); + /** + * @return rop-form basic block list + */ + private BasicBlockList convertBasicBlocks() { + ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); - // Don't count the exit block, if it exists and is reachable. - ropBlockCount -= (exitBlock != null && exitBlock.isReachable()) ? 1 : 0; + // Exit block may be null. + SsaBasicBlock exitBlock = ssaMeth.getExitBlock(); - BasicBlockList result = new BasicBlockList(ropBlockCount); + ssaMeth.computeReachability(); + int ropBlockCount = ssaMeth.getCountReachableBlocks(); - // Convert all the reachable blocks except the exit block. - int ropBlockIndex = 0; - for (SsaBasicBlock b : blocks) { - if (b.isReachable() && b != exitBlock) { - result.set(ropBlockIndex++, convertBasicBlock(b)); - } - } + // Don't count the exit block, if it exists and is reachable. + ropBlockCount -= (exitBlock != null && exitBlock.isReachable()) ? 1 : 0; - // The exit block, which is discarded, must do nothing. - if (exitBlock != null && exitBlock.getInsns().size() != 0) { - throw new RuntimeException( - "Exit block must have no insns when leaving SSA form"); - } + BasicBlockList result = new BasicBlockList(ropBlockCount); - return result; + // Convert all the reachable blocks except the exit block. + int ropBlockIndex = 0; + for (SsaBasicBlock b : blocks) { + if (b.isReachable() && b != exitBlock) { + result.set(ropBlockIndex++, convertBasicBlock(b)); + } } - /** - * Validates that a basic block is a valid end predecessor. It must - * end in a RETURN or a THROW. Throws a runtime exception on error. - * - * @param b {@code non-null;} block to validate - * @throws RuntimeException on error - */ - private void verifyValidExitPredecessor(SsaBasicBlock b) { - ArrayList<SsaInsn> insns = b.getInsns(); - SsaInsn lastInsn = insns.get(insns.size() - 1); - Rop opcode = lastInsn.getOpcode(); + // The exit block, which is discarded, must do nothing. + if (exitBlock != null && exitBlock.getInsns().size() != 0) { + throw new RuntimeException("Exit block must have no insns when leaving SSA form"); + } - if (opcode.getBranchingness() != Rop.BRANCH_RETURN - && opcode != Rops.THROW) { - throw new RuntimeException("Exit predecessor must end" - + " in valid exit statement."); - } + return result; + } + + /** + * Validates that a basic block is a valid end predecessor. It must + * end in a RETURN or a THROW. Throws a runtime exception on error. + * + * @param b {@code non-null;} block to validate + * @throws RuntimeException on error + */ + private void verifyValidExitPredecessor(SsaBasicBlock b) { + ArrayList<SsaInsn> insns = b.getInsns(); + SsaInsn lastInsn = insns.get(insns.size() - 1); + Rop opcode = lastInsn.getOpcode(); + + if (opcode.getBranchingness() != Rop.BRANCH_RETURN && opcode != Rops.THROW) { + throw new RuntimeException("Exit predecessor must end" + " in valid exit statement."); + } + } + + /** + * Converts a single basic block to rop form. + * + * @param block SSA block to process + * @return {@code non-null;} ROP block + */ + private BasicBlock convertBasicBlock(SsaBasicBlock block) { + IntList successorList = block.getRopLabelSuccessorList(); + int primarySuccessorLabel = block.getPrimarySuccessorRopLabel(); + + // Filter out any reference to the SSA form's exit block. + + // Exit block may be null. + SsaBasicBlock exitBlock = ssaMeth.getExitBlock(); + int exitRopLabel = (exitBlock == null) ? -1 : exitBlock.getRopLabel(); + + if (successorList.contains(exitRopLabel)) { + if (successorList.size() > 1) { + throw new RuntimeException( + "Exit predecessor must have no other successors" + Hex.u2(block.getRopLabel())); + } else { + successorList = IntList.EMPTY; + primarySuccessorLabel = -1; + + verifyValidExitPredecessor(block); + } } - /** - * Converts a single basic block to rop form. - * - * @param block SSA block to process - * @return {@code non-null;} ROP block - */ - private BasicBlock convertBasicBlock(SsaBasicBlock block) { - IntList successorList = block.getRopLabelSuccessorList(); - int primarySuccessorLabel = block.getPrimarySuccessorRopLabel(); - - // Filter out any reference to the SSA form's exit block. - - // Exit block may be null. - SsaBasicBlock exitBlock = ssaMeth.getExitBlock(); - int exitRopLabel = (exitBlock == null) ? -1 : exitBlock.getRopLabel(); - - if (successorList.contains(exitRopLabel)) { - if (successorList.size() > 1) { - throw new RuntimeException( - "Exit predecessor must have no other successors" - + Hex.u2(block.getRopLabel())); - } else { - successorList = IntList.EMPTY; - primarySuccessorLabel = -1; - - verifyValidExitPredecessor(block); - } - } + successorList.setImmutable(); + + BasicBlock result = new BasicBlock(block.getRopLabel(), convertInsns(block.getInsns()), + successorList, primarySuccessorLabel); - successorList.setImmutable(); + return result; + } - BasicBlock result = new BasicBlock( - block.getRopLabel(), convertInsns(block.getInsns()), - successorList, - primarySuccessorLabel); + /** + * Converts an insn list to rop form. + * + * @param ssaInsns {@code non-null;} old instructions + * @return {@code non-null;} immutable instruction list + */ + private InsnList convertInsns(ArrayList<SsaInsn> ssaInsns) { + int insnCount = ssaInsns.size(); + InsnList result = new InsnList(insnCount); - return result; + for (int i = 0; i < insnCount; i++) { + result.set(i, ssaInsns.get(i).toRopInsn()); } - /** - * Converts an insn list to rop form. - * - * @param ssaInsns {@code non-null;} old instructions - * @return {@code non-null;} immutable instruction list - */ - private InsnList convertInsns(ArrayList<SsaInsn> ssaInsns) { - int insnCount = ssaInsns.size(); - InsnList result = new InsnList(insnCount); + result.setImmutable(); - for (int i = 0; i < insnCount; i++) { - result.set(i, ssaInsns.get(i).toRopInsn()); - } + return result; + } - result.setImmutable(); + /** + * <b>Note:</b> This method is not presently used. + * + * @return a list of registers ordered by most-frequently-used to + * least-frequently-used. Each register is listed once and only + * once. + */ + public int[] getRegistersByFrequency() { + int regCount = ssaMeth.getRegCount(); + Integer[] ret = new Integer[regCount]; - return result; + for (int i = 0; i < regCount; i++) { + ret[i] = i; } - /** - * <b>Note:</b> This method is not presently used. - * - * @return a list of registers ordered by most-frequently-used to - * least-frequently-used. Each register is listed once and only - * once. - */ - public int[] getRegistersByFrequency() { - int regCount = ssaMeth.getRegCount(); - Integer[] ret = new Integer[regCount]; + Arrays.sort(ret, new Comparator<Integer>() { + @Override + public int compare(Integer o1, Integer o2) { + return ssaMeth.getUseListForRegister(o2).size() - ssaMeth.getUseListForRegister(o1).size(); + } + }); - for (int i = 0; i < regCount; i++) { - ret[i] = i; - } - - Arrays.sort(ret, new Comparator<Integer>() { - public int compare(Integer o1, Integer o2) { - return ssaMeth.getUseListForRegister(o2).size() - - ssaMeth.getUseListForRegister(o1).size(); - } - }); - - int result[] = new int[regCount]; + int result[] = new int[regCount]; - for (int i = 0; i < regCount; i++) { - result[i] = ret[i]; - } - - return result; + for (int i = 0; i < regCount; i++) { + result[i] = ret[i]; } + + return result; + } } diff --git a/dx/src/com/android/jack/dx/util/AnnotatedOutput.java b/dx/src/com/android/jack/dx/util/AnnotatedOutput.java index 6bbe4b7..b8374b4 100644 --- a/dx/src/com/android/jack/dx/util/AnnotatedOutput.java +++ b/dx/src/com/android/jack/dx/util/AnnotatedOutput.java @@ -20,60 +20,59 @@ package com.android.jack.dx.util; * Interface for a binary output destination that may be augmented * with textual annotations. */ -public interface AnnotatedOutput - extends Output { - /** - * Get whether this instance will actually keep annotations. - * - * @return {@code true} iff annotations are being kept - */ - public boolean annotates(); +public interface AnnotatedOutput extends Output { + /** + * Get whether this instance will actually keep annotations. + * + * @return {@code true} 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} iff annotations are to be verbose - */ - public boolean isVerbose(); + /** + * 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} 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 {@code non-null;} the annotation message - */ - public void annotate(String msg); + /** + * 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 {@code 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 {@code >= 0;} the amount of output for this annotation to - * cover - * @param msg {@code non-null;} the annotation message - */ - public void annotate(int amt, 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 {@code >= 0;} the amount of output for this annotation to + * cover + * @param msg {@code 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(); + /** + * 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 {@code >= 1;} the maximum width - */ - public int getAnnotationWidth(); + /** + * 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 {@code >= 1;} the maximum width + */ + public int getAnnotationWidth(); } diff --git a/dx/src/com/android/jack/dx/util/BitIntSet.java b/dx/src/com/android/jack/dx/util/BitIntSet.java index a4f5156..b19a387 100644 --- a/dx/src/com/android/jack/dx/util/BitIntSet.java +++ b/dx/src/com/android/jack/dx/util/BitIntSet.java @@ -23,123 +23,129 @@ import java.util.NoSuchElementException; */ public class BitIntSet implements IntSet { - /** also accessed in ListIntSet */ - int[] bits; - - /** - * Constructs an instance. - * - * @param max the maximum value of ints in this set. - */ - public BitIntSet(int max) { - bits = Bits.makeBitSet(max); + /** also accessed in ListIntSet */ + int[] bits; + + /** + * Constructs an instance. + * + * @param max the maximum value of ints in this set. + */ + public BitIntSet(int max) { + bits = Bits.makeBitSet(max); + } + + /** @inheritDoc */ + @Override + public void add(int value) { + ensureCapacity(value); + Bits.set(bits, value, true); + } + + /** + * Ensures that the bit set has the capacity to represent the given value. + * + * @param value {@code >= 0;} value to represent + */ + private void ensureCapacity(int value) { + if (value >= Bits.getMax(bits)) { + int[] newBits = Bits.makeBitSet(Math.max(value + 1, 2 * Bits.getMax(bits))); + System.arraycopy(bits, 0, newBits, 0, bits.length); + bits = newBits; } + } - /** @inheritDoc */ - public void add(int value) { - ensureCapacity(value); - Bits.set(bits, value, true); + /** @inheritDoc */ + @Override + public void remove(int value) { + if (value < Bits.getMax(bits)) { + Bits.set(bits, value, false); } - - /** - * Ensures that the bit set has the capacity to represent the given value. - * - * @param value {@code >= 0;} value to represent - */ - private void ensureCapacity(int value) { - if (value >= Bits.getMax(bits)) { - int[] newBits = Bits.makeBitSet( - Math.max(value + 1, 2 * Bits.getMax(bits))); - System.arraycopy(bits, 0, newBits, 0, bits.length); - bits = newBits; - } - } - - /** @inheritDoc */ - public void remove(int value) { - if (value < Bits.getMax(bits)) { - Bits.set(bits, value, false); - } - } - - /** @inheritDoc */ - public boolean has(int value) { - return (value < Bits.getMax(bits)) && Bits.get(bits, value); + } + + /** @inheritDoc */ + @Override + public boolean has(int value) { + return (value < Bits.getMax(bits)) && Bits.get(bits, value); + } + + /** @inheritDoc */ + @Override + public void merge(IntSet other) { + if (other instanceof BitIntSet) { + BitIntSet o = (BitIntSet) other; + ensureCapacity(Bits.getMax(o.bits) + 1); + Bits.or(bits, o.bits); + } else if (other instanceof ListIntSet) { + ListIntSet o = (ListIntSet) other; + int sz = o.ints.size(); + + if (sz > 0) { + ensureCapacity(o.ints.get(sz - 1)); + } + for (int i = 0; i < o.ints.size(); i++) { + Bits.set(bits, o.ints.get(i), true); + } + } else { + IntIterator iter = other.iterator(); + while (iter.hasNext()) { + add(iter.next()); + } } - - /** @inheritDoc */ - public void merge(IntSet other) { - if (other instanceof BitIntSet) { - BitIntSet o = (BitIntSet) other; - ensureCapacity(Bits.getMax(o.bits) + 1); - Bits.or(bits, o.bits); - } else if (other instanceof ListIntSet) { - ListIntSet o = (ListIntSet) other; - int sz = o.ints.size(); - - if (sz > 0) { - ensureCapacity(o.ints.get(sz - 1)); - } - for (int i = 0; i < o.ints.size(); i++) { - Bits.set(bits, o.ints.get(i), true); - } - } else { - IntIterator iter = other.iterator(); - while (iter.hasNext()) { - add(iter.next()); - } + } + + /** @inheritDoc */ + @Override + public int elements() { + return Bits.bitCount(bits); + } + + /** @inheritDoc */ + @Override + public IntIterator iterator() { + return new IntIterator() { + private int idx = Bits.findFirst(bits, 0); + + /** @inheritDoc */ + @Override + public boolean hasNext() { + return idx >= 0; + } + + /** @inheritDoc */ + @Override + public int next() { + if (!hasNext()) { + throw new NoSuchElementException(); } - } - - /** @inheritDoc */ - public int elements() { - return Bits.bitCount(bits); - } - /** @inheritDoc */ - public IntIterator iterator() { - return new IntIterator() { - private int idx = Bits.findFirst(bits, 0); + int ret = idx; - /** @inheritDoc */ - public boolean hasNext() { - return idx >= 0; - } + idx = Bits.findFirst(bits, idx + 1); - /** @inheritDoc */ - public int next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } + return ret; + } + }; + } - int ret = idx; + /** @inheritDoc */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); - idx = Bits.findFirst(bits, idx+1); + sb.append('{'); - return ret; - } - }; + boolean first = true; + for (int i = Bits.findFirst(bits, 0); i >= 0; i = Bits.findFirst(bits, i + 1)) { + if (!first) { + sb.append(", "); + } + first = false; + sb.append(i); } - /** @inheritDoc */ - public String toString() { - StringBuilder sb = new StringBuilder(); - - sb.append('{'); - - boolean first = true; - for (int i = Bits.findFirst(bits, 0) - ; i >= 0 - ; i = Bits.findFirst(bits, i + 1)) { - if (!first) { - sb.append(", "); - } - first = false; - sb.append(i); - } - - sb.append('}'); + sb.append('}'); - return sb.toString(); - } + return sb.toString(); + } } diff --git a/dx/src/com/android/jack/dx/util/Bits.java b/dx/src/com/android/jack/dx/util/Bits.java index 629d5cf..ea77735 100644 --- a/dx/src/com/android/jack/dx/util/Bits.java +++ b/dx/src/com/android/jack/dx/util/Bits.java @@ -20,217 +20,217 @@ package com.android.jack.dx.util; * Utilities for treating {@code int[]}s as bit sets. */ public final class Bits { - /** - * This class is uninstantiable. - */ - private Bits() { - // This space intentionally left blank. + /** + * This class is uninstantiable. + */ + private Bits() { + // This space intentionally left blank. + } + + /** + * Constructs a bit set to contain bits up to the given index (exclusive). + * + * @param max {@code >= 0;} the maximum bit index (exclusive) + * @return {@code non-null;} an appropriately-constructed instance + */ + public static int[] makeBitSet(int max) { + int size = (max + 0x1f) >> 5; + return new int[size]; + } + + /** + * Gets the maximum index (exclusive) for the given bit set. + * + * @param bits {@code non-null;} bit set in question + * @return {@code >= 0;} the maximum index (exclusive) that may be set + */ + public static int getMax(int[] bits) { + return bits.length * 0x20; + } + + /** + * Gets the value of the bit at the given index. + * + * @param bits {@code non-null;} bit set to operate on + * @param idx {@code >= 0, < getMax(set);} which bit + * @return the value of the indicated bit + */ + public static boolean get(int[] bits, int idx) { + int arrayIdx = idx >> 5; + int bit = 1 << (idx & 0x1f); + return (bits[arrayIdx] & bit) != 0; + } + + /** + * Sets the given bit to the given value. + * + * @param bits {@code non-null;} bit set to operate on + * @param idx {@code >= 0, < getMax(set);} which bit + * @param value the new value for the bit + */ + public static void set(int[] bits, int idx, boolean value) { + int arrayIdx = idx >> 5; + int bit = 1 << (idx & 0x1f); + + if (value) { + bits[arrayIdx] |= bit; + } else { + bits[arrayIdx] &= ~bit; } - - /** - * Constructs a bit set to contain bits up to the given index (exclusive). - * - * @param max {@code >= 0;} the maximum bit index (exclusive) - * @return {@code non-null;} an appropriately-constructed instance - */ - public static int[] makeBitSet(int max) { - int size = (max + 0x1f) >> 5; - return new int[size]; - } - - /** - * Gets the maximum index (exclusive) for the given bit set. - * - * @param bits {@code non-null;} bit set in question - * @return {@code >= 0;} the maximum index (exclusive) that may be set - */ - public static int getMax(int[] bits) { - return bits.length * 0x20; - } - - /** - * Gets the value of the bit at the given index. - * - * @param bits {@code non-null;} bit set to operate on - * @param idx {@code >= 0, < getMax(set);} which bit - * @return the value of the indicated bit - */ - public static boolean get(int[] bits, int idx) { - int arrayIdx = idx >> 5; - int bit = 1 << (idx & 0x1f); - return (bits[arrayIdx] & bit) != 0; - } - - /** - * Sets the given bit to the given value. - * - * @param bits {@code non-null;} bit set to operate on - * @param idx {@code >= 0, < getMax(set);} which bit - * @param value the new value for the bit - */ - public static void set(int[] bits, int idx, boolean value) { - int arrayIdx = idx >> 5; - int bit = 1 << (idx & 0x1f); - - if (value) { - bits[arrayIdx] |= bit; - } else { - bits[arrayIdx] &= ~bit; - } - } - - /** - * Sets the given bit to {@code true}. - * - * @param bits {@code non-null;} bit set to operate on - * @param idx {@code >= 0, < getMax(set);} which bit - */ - public static void set(int[] bits, int idx) { - int arrayIdx = idx >> 5; - int bit = 1 << (idx & 0x1f); - bits[arrayIdx] |= bit; - } - - /** - * Sets the given bit to {@code false}. - * - * @param bits {@code non-null;} bit set to operate on - * @param idx {@code >= 0, < getMax(set);} which bit - */ - public static void clear(int[] bits, int idx) { - int arrayIdx = idx >> 5; - int bit = 1 << (idx & 0x1f); - bits[arrayIdx] &= ~bit; + } + + /** + * Sets the given bit to {@code true}. + * + * @param bits {@code non-null;} bit set to operate on + * @param idx {@code >= 0, < getMax(set);} which bit + */ + public static void set(int[] bits, int idx) { + int arrayIdx = idx >> 5; + int bit = 1 << (idx & 0x1f); + bits[arrayIdx] |= bit; + } + + /** + * Sets the given bit to {@code false}. + * + * @param bits {@code non-null;} bit set to operate on + * @param idx {@code >= 0, < getMax(set);} which bit + */ + public static void clear(int[] bits, int idx) { + int arrayIdx = idx >> 5; + int bit = 1 << (idx & 0x1f); + bits[arrayIdx] &= ~bit; + } + + /** + * Returns whether or not the given bit set is empty, that is, whether + * no bit is set to {@code true}. + * + * @param bits {@code non-null;} bit set to operate on + * @return {@code true} iff all bits are {@code false} + */ + public static boolean isEmpty(int[] bits) { + int len = bits.length; + + for (int i = 0; i < len; i++) { + if (bits[i] != 0) { + return false; + } } - /** - * Returns whether or not the given bit set is empty, that is, whether - * no bit is set to {@code true}. - * - * @param bits {@code non-null;} bit set to operate on - * @return {@code true} iff all bits are {@code false} - */ - public static boolean isEmpty(int[] bits) { - int len = bits.length; - - for (int i = 0; i < len; i++) { - if (bits[i] != 0) { - return false; - } - } - - return true; + return true; + } + + /** + * Gets the number of bits set to {@code true} in the given bit set. + * + * @param bits {@code non-null;} bit set to operate on + * @return {@code >= 0;} the bit count (aka population count) of the set + */ + public static int bitCount(int[] bits) { + int len = bits.length; + int count = 0; + + for (int i = 0; i < len; i++) { + count += Integer.bitCount(bits[i]); } - /** - * Gets the number of bits set to {@code true} in the given bit set. - * - * @param bits {@code non-null;} bit set to operate on - * @return {@code >= 0;} the bit count (aka population count) of the set - */ - public static int bitCount(int[] bits) { - int len = bits.length; - int count = 0; - - for (int i = 0; i < len; i++) { - count += Integer.bitCount(bits[i]); + return count; + } + + /** + * Returns whether any bits are set to {@code true} in the + * specified range. + * + * @param bits {@code non-null;} bit set to operate on + * @param start {@code >= 0;} index of the first bit in the range (inclusive) + * @param end {@code >= 0;} index of the last bit in the range (exclusive) + * @return {@code true} if any bit is set to {@code true} in + * the indicated range + */ + public static boolean anyInRange(int[] bits, int start, int end) { + int idx = findFirst(bits, start); + return (idx >= 0) && (idx < end); + } + + /** + * Finds the lowest-order bit set at or after the given index in the + * given bit set. + * + * @param bits {@code non-null;} bit set to operate on + * @param idx {@code >= 0;} minimum index to return + * @return {@code >= -1;} lowest-order bit set at or after {@code idx}, + * or {@code -1} if there is no appropriate bit index to return + */ + public static int findFirst(int[] bits, int idx) { + int len = bits.length; + int minBit = idx & 0x1f; + + for (int arrayIdx = idx >> 5; arrayIdx < len; arrayIdx++) { + int word = bits[arrayIdx]; + if (word != 0) { + int bitIdx = findFirst(word, minBit); + if (bitIdx >= 0) { + return (arrayIdx << 5) + bitIdx; } - - return count; + } + minBit = 0; } - /** - * Returns whether any bits are set to {@code true} in the - * specified range. - * - * @param bits {@code non-null;} bit set to operate on - * @param start {@code >= 0;} index of the first bit in the range (inclusive) - * @param end {@code >= 0;} index of the last bit in the range (exclusive) - * @return {@code true} if any bit is set to {@code true} in - * the indicated range - */ - public static boolean anyInRange(int[] bits, int start, int end) { - int idx = findFirst(bits, start); - return (idx >= 0) && (idx < end); + return -1; + } + + /** + * Finds the lowest-order bit set at or after the given index in the + * given {@code int}. + * + * @param value the value in question + * @param idx 0..31 the minimum bit index to return + * @return {@code >= -1;} lowest-order bit set at or after {@code idx}, + * or {@code -1} if there is no appropriate bit index to return + */ + public static int findFirst(int value, int idx) { + value &= ~((1 << idx) - 1); // Mask off too-low bits. + int result = Integer.numberOfTrailingZeros(value); + return (result == 32) ? -1 : result; + } + + /** + * Ors bit array {@code b} into bit array {@code a}. + * {@code a.length} must be greater than or equal to + * {@code b.length}. + * + * @param a {@code non-null;} int array to be ored with other argument. This + * argument is modified. + * @param b {@code non-null;} int array to be ored into {@code a}. This + * argument is not modified. + */ + public static void or(int[] a, int[] b) { + for (int i = 0; i < b.length; i++) { + a[i] |= b[i]; } + } - /** - * Finds the lowest-order bit set at or after the given index in the - * given bit set. - * - * @param bits {@code non-null;} bit set to operate on - * @param idx {@code >= 0;} minimum index to return - * @return {@code >= -1;} lowest-order bit set at or after {@code idx}, - * or {@code -1} if there is no appropriate bit index to return - */ - public static int findFirst(int[] bits, int idx) { - int len = bits.length; - int minBit = idx & 0x1f; - - for (int arrayIdx = idx >> 5; arrayIdx < len; arrayIdx++) { - int word = bits[arrayIdx]; - if (word != 0) { - int bitIdx = findFirst(word, minBit); - if (bitIdx >= 0) { - return (arrayIdx << 5) + bitIdx; - } - } - minBit = 0; - } + public static String toHuman(int[] bits) { + StringBuilder sb = new StringBuilder(); - return -1; - } + boolean needsComma = false; - /** - * Finds the lowest-order bit set at or after the given index in the - * given {@code int}. - * - * @param value the value in question - * @param idx 0..31 the minimum bit index to return - * @return {@code >= -1;} lowest-order bit set at or after {@code idx}, - * or {@code -1} if there is no appropriate bit index to return - */ - public static int findFirst(int value, int idx) { - value &= ~((1 << idx) - 1); // Mask off too-low bits. - int result = Integer.numberOfTrailingZeros(value); - return (result == 32) ? -1 : result; - } + sb.append('{'); - /** - * Ors bit array {@code b} into bit array {@code a}. - * {@code a.length} must be greater than or equal to - * {@code b.length}. - * - * @param a {@code non-null;} int array to be ored with other argument. This - * argument is modified. - * @param b {@code non-null;} int array to be ored into {@code a}. This - * argument is not modified. - */ - public static void or(int[] a, int[] b) { - for (int i = 0; i < b.length; i++) { - a[i] |= b[i]; + int bitsLength = 32 * bits.length; + for (int i = 0; i < bitsLength; i++) { + if (Bits.get(bits, i)) { + if (needsComma) { + sb.append(','); } + needsComma = true; + sb.append(i); + } } + sb.append('}'); - public static String toHuman(int[] bits) { - StringBuilder sb = new StringBuilder(); - - boolean needsComma = false; - - sb.append('{'); - - int bitsLength = 32 * bits.length; - for (int i = 0; i < bitsLength; i++) { - if (Bits.get(bits, i)) { - if (needsComma) { - sb.append(','); - } - needsComma = true; - sb.append(i); - } - } - sb.append('}'); - - return sb.toString(); - } + return sb.toString(); + } } diff --git a/dx/src/com/android/jack/dx/util/ByteArray.java b/dx/src/com/android/jack/dx/util/ByteArray.java index b5ff800..e696542 100644 --- a/dx/src/com/android/jack/dx/util/ByteArray.java +++ b/dx/src/com/android/jack/dx/util/ByteArray.java @@ -27,335 +27,328 @@ import java.io.InputStream; * <b>Note:</b> Multibyte accessors all use big-endian order. */ public final class ByteArray { - /** {@code non-null;} underlying array */ - private final byte[] bytes; - - /** {@code >= 0}; start index of the slice (inclusive) */ - private final int start; - - /** {@code >= 0, <= bytes.length}; size computed as - * {@code end - start} (in the constructor) */ - private final int size; - - /** - * Constructs an instance. - * - * @param bytes {@code non-null;} the underlying array - * @param start {@code >= 0;} start index of the slice (inclusive) - * @param end {@code >= start, <= bytes.length;} 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; + /** {@code non-null;} underlying array */ + private final byte[] bytes; + + /** {@code >= 0}; start index of the slice (inclusive) */ + private final int start; + + /** {@code >= 0, <= bytes.length}; size computed as + * {@code end - start} (in the constructor) */ + private final int size; + + /** + * Constructs an instance. + * + * @param bytes {@code non-null;} the underlying array + * @param start {@code >= 0;} start index of the slice (inclusive) + * @param end {@code >= start, <= bytes.length;} end index of + * the slice (exclusive) + */ + public ByteArray(byte[] bytes, int start, int end) { + if (bytes == null) { + throw new NullPointerException("bytes == null"); } - /** - * Constructs an instance from an entire {@code byte[]}. - * - * @param bytes {@code non-null;} the underlying array - */ - public ByteArray(byte[] bytes) { - this(bytes, 0, bytes.length); + if (start < 0) { + throw new IllegalArgumentException("start < 0"); } - /** - * Gets the size of the array, in bytes. - * - * @return {@code >= 0;} the size - */ - public int size() { - return size; + if (end < start) { + throw new IllegalArgumentException("end < start"); } - /** - * Returns a slice (that is, a sub-array) of this instance. - * - * @param start {@code >= 0;} start index of the slice (inclusive) - * @param end {@code >= start, <= size();} end index of - * the slice (exclusive) - * @return {@code non-null;} the slice - */ - public ByteArray slice(int start, int end) { - checkOffsets(start, end); - return new ByteArray(bytes, start + this.start, end + this.start); + if (end > bytes.length) { + throw new IllegalArgumentException("end > bytes.length"); } - /** - * Returns the offset into the given array represented by the given - * offset into this instance. - * - * @param offset offset into this instance - * @param bytes {@code non-null;} (alleged) underlying array - * @return corresponding offset into {@code bytes} - * @throws IllegalArgumentException thrown if {@code bytes} 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; + this.bytes = bytes; + this.start = start; + this.size = end - start; + } + + /** + * Constructs an instance from an entire {@code byte[]}. + * + * @param bytes {@code non-null;} the underlying array + */ + public ByteArray(byte[] bytes) { + this(bytes, 0, bytes.length); + } + + /** + * Gets the size of the array, in bytes. + * + * @return {@code >= 0;} the size + */ + public int size() { + return size; + } + + /** + * Returns a slice (that is, a sub-array) of this instance. + * + * @param start {@code >= 0;} start index of the slice (inclusive) + * @param end {@code >= start, <= size();} end index of + * the slice (exclusive) + * @return {@code 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 {@code non-null;} (alleged) underlying array + * @return corresponding offset into {@code bytes} + * @throws IllegalArgumentException thrown if {@code bytes} is + * not the underlying array of this instance + */ + public int underlyingOffset(int offset, byte[] bytes) { + if (bytes != this.bytes) { + throw new IllegalArgumentException("wrong bytes"); } - /** - * Gets the {@code signed byte} value at a particular offset. - * - * @param off {@code >= 0, < size();} offset to fetch - * @return {@code signed byte} at that offset - */ - public int getByte(int off) { - checkOffsets(off, off + 1); - return getByte0(off); - } - - /** - * Gets the {@code signed short} value at a particular offset. - * - * @param off {@code >= 0, < (size() - 1);} offset to fetch - * @return {@code signed short} at that offset - */ - public int getShort(int off) { - checkOffsets(off, off + 2); - return (getByte0(off) << 8) | getUnsignedByte0(off + 1); - } - - /** - * Gets the {@code signed int} value at a particular offset. - * - * @param off {@code >= 0, < (size() - 3);} offset to fetch - * @return {@code signed int} 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} value at a particular offset. - * - * @param off {@code >= 0, < (size() - 7);} offset to fetch - * @return {@code signed int} 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; + return start + offset; + } + + /** + * Gets the {@code signed byte} value at a particular offset. + * + * @param off {@code >= 0, < size();} offset to fetch + * @return {@code signed byte} at that offset + */ + public int getByte(int off) { + checkOffsets(off, off + 1); + return getByte0(off); + } + + /** + * Gets the {@code signed short} value at a particular offset. + * + * @param off {@code >= 0, < (size() - 1);} offset to fetch + * @return {@code signed short} at that offset + */ + public int getShort(int off) { + checkOffsets(off, off + 2); + return (getByte0(off) << 8) | getUnsignedByte0(off + 1); + } + + /** + * Gets the {@code signed int} value at a particular offset. + * + * @param off {@code >= 0, < (size() - 3);} offset to fetch + * @return {@code signed int} 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} value at a particular offset. + * + * @param off {@code >= 0, < (size() - 7);} offset to fetch + * @return {@code signed int} 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} value at a particular offset. + * + * @param off {@code >= 0, < size();} offset to fetch + * @return {@code unsigned byte} at that offset + */ + public int getUnsignedByte(int off) { + checkOffsets(off, off + 1); + return getUnsignedByte0(off); + } + + /** + * Gets the {@code unsigned short} value at a particular offset. + * + * @param off {@code >= 0, < (size() - 1);} offset to fetch + * @return {@code unsigned short} 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[]} at the given offset. The given array must be + * large enough. + * + * @param out {@code non-null;} array to hold the output + * @param offset {@code non-null;} index into {@code out} 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()"); } - /** - * Gets the {@code unsigned byte} value at a particular offset. - * - * @param off {@code >= 0, < size();} offset to fetch - * @return {@code unsigned byte} at that offset - */ - public int getUnsignedByte(int off) { - checkOffsets(off, off + 1); - return getUnsignedByte0(off); + 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} 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} value at the given offset, + * without doing any argument checking. + * + * @param off offset to fetch + * @return byte at that offset + */ + private int getUnsignedByte0(int off) { + return bytes[start + off] & 0xff; + } + + /** + * Gets a {@code DataInputStream} that reads from this instance, + * with the cursor starting at the beginning of this instance's data. + * <b>Note:</b> The returned instance may be cast to {@link #GetCursor} + * if needed. + * + * @return {@code non-null;} an appropriately-constructed + * {@code DataInputStream} instance + */ + public MyDataInputStream makeDataInputStream() { + return new MyDataInputStream(makeInputStream()); + } + + /** + * Gets a {@code InputStream} that reads from this instance, + * with the cursor starting at the beginning of this instance's data. + * <b>Note:</b> The returned instance may be cast to {@link #GetCursor} + * if needed. + * + * @return {@code non-null;} an appropriately-constructed + * {@code InputStream} instancex + */ + public MyInputStream makeInputStream() { + return new MyInputStream(); + } + + /** + * Helper interface that allows one to get the cursor (of a stream). + */ + public interface GetCursor { /** - * Gets the {@code unsigned short} value at a particular offset. + * Gets the current cursor. * - * @param off {@code >= 0, < (size() - 1);} offset to fetch - * @return {@code unsigned short} at that offset + * @return {@code 0..size();} the cursor */ - public int getUnsignedShort(int off) { - checkOffsets(off, off + 2); - return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1); + public int getCursor(); + } + + /** + * Helper class for {@link #makeInputStream}, which implements the + * stream functionality. + */ + public class MyInputStream extends InputStream { + /** 0..size; the cursor */ + private int cursor; + + /** 0..size; the mark */ + private int mark; + + public MyInputStream() { + cursor = 0; + mark = 0; } - /** - * Copies the contents of this instance into the given raw - * {@code byte[]} at the given offset. The given array must be - * large enough. - * - * @param out {@code non-null;} array to hold the output - * @param offset {@code non-null;} index into {@code out} 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()"); - } + @Override + public int read() throws IOException { + if (cursor >= size) { + return -1; + } - System.arraycopy(bytes, start, out, offset, size); + int result = getUnsignedByte0(cursor); + cursor++; + return result; } - /** - * 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); - } - } + @Override + public int read(byte[] arr, int offset, int length) { + if ((offset + length) > arr.length) { + length = arr.length - offset; + } - /** - * Gets the {@code signed byte} 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]; - } + int maxLength = size - cursor; + if (length > maxLength) { + length = maxLength; + } - /** - * Gets the {@code unsigned byte} 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; + System.arraycopy(bytes, cursor + start, arr, offset, length); + cursor += length; + return length; } - /** - * Gets a {@code DataInputStream} that reads from this instance, - * with the cursor starting at the beginning of this instance's data. - * <b>Note:</b> The returned instance may be cast to {@link #GetCursor} - * if needed. - * - * @return {@code non-null;} an appropriately-constructed - * {@code DataInputStream} instance - */ - public MyDataInputStream makeDataInputStream() { - return new MyDataInputStream(makeInputStream()); + @Override + public int available() { + return size - cursor; } - /** - * Gets a {@code InputStream} that reads from this instance, - * with the cursor starting at the beginning of this instance's data. - * <b>Note:</b> The returned instance may be cast to {@link #GetCursor} - * if needed. - * - * @return {@code non-null;} an appropriately-constructed - * {@code InputStream} instancex - */ - public MyInputStream makeInputStream() { - return new MyInputStream(); + @Override + public void mark(int reserve) { + mark = cursor; } - /** - * Helper interface that allows one to get the cursor (of a stream). - */ - public interface GetCursor { - /** - * Gets the current cursor. - * - * @return {@code 0..size();} the cursor - */ - public int getCursor(); + @Override + public void reset() { + cursor = mark; } - /** - * Helper class for {@link #makeInputStream}, which implements the - * stream functionality. - */ - public class MyInputStream extends InputStream { - /** 0..size; the cursor */ - private int cursor; - - /** 0..size; the mark */ - private int mark; - - public MyInputStream() { - cursor = 0; - mark = 0; - } - - public int read() throws IOException { - if (cursor >= size) { - return -1; - } - - int result = getUnsignedByte0(cursor); - cursor++; - return result; - } - - public int read(byte[] arr, int offset, int length) { - if ((offset + length) > arr.length) { - length = arr.length - offset; - } - - int maxLength = size - cursor; - if (length > maxLength) { - length = maxLength; - } - - System.arraycopy(bytes, cursor + start, arr, offset, length); - cursor += length; - return length; - } - - public int available() { - return size - cursor; - } - - public void mark(int reserve) { - mark = cursor; - } - - public void reset() { - cursor = mark; - } - - public boolean markSupported() { - return true; - } + @Override + public boolean markSupported() { + return true; } - - /** - * Helper class for {@link #makeDataInputStream}. This is used - * simply so that the cursor of a wrapped {@link #MyInputStream} - * instance may be easily determined. - */ - public static class MyDataInputStream extends DataInputStream { - /** {@code non-null;} the underlying {@link #MyInputStream} */ - private final MyInputStream wrapped; - - public MyDataInputStream(MyInputStream wrapped) { - super(wrapped); - - this.wrapped = wrapped; - } + } + + /** + * Helper class for {@link #makeDataInputStream}. This is used + * simply so that the cursor of a wrapped {@link #MyInputStream} + * instance may be easily determined. + */ + public static class MyDataInputStream extends DataInputStream { + public MyDataInputStream(MyInputStream wrapped) { + super(wrapped); } + } } diff --git a/dx/src/com/android/jack/dx/util/ByteArrayAnnotatedOutput.java b/dx/src/com/android/jack/dx/util/ByteArrayAnnotatedOutput.java index 09174e0..fcfab9f 100644 --- a/dx/src/com/android/jack/dx/util/ByteArrayAnnotatedOutput.java +++ b/dx/src/com/android/jack/dx/util/ByteArrayAnnotatedOutput.java @@ -27,606 +27,622 @@ import java.util.ArrayList; * <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, ByteOutput { - /** 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; - - /** {@code non-null;} the data itself */ - private byte[] data; - - /** {@code >= 0;} current output cursor */ - private int cursor; - - /** whether annotations are to be verbose */ - private boolean verbose; - - /** - * {@code null-ok;} list of annotations, or {@code null} if this instance - * isn't keeping them - */ - private ArrayList<Annotation> annotations; - - /** {@code >= 40 (if used);} the desired maximum annotation width */ - private int annotationWidth; - - /** - * {@code >= 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 {@code non-null;} data array to use for output - */ - public ByteArrayAnnotatedOutput(byte[] data) { - this(data, false); +public final class ByteArrayAnnotatedOutput implements AnnotatedOutput, ByteOutput { + /** 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; + + /** {@code non-null;} the data itself */ + private byte[] data; + + /** {@code >= 0;} current output cursor */ + private int cursor; + + /** whether annotations are to be verbose */ + private boolean verbose; + + /** + * {@code null-ok;} list of annotations, or {@code null} if this instance + * isn't keeping them + */ + private ArrayList<Annotation> annotations; + + /** {@code >= 40 (if used);} the desired maximum annotation width */ + private int annotationWidth; + + /** + * {@code >= 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 {@code 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(DEFAULT_SIZE); + } + + /** + * Constructs a "stretchy" instance with initial size {@code size}. The + * underlying array may be reallocated. The constructed instance does not + * keep annotations by default. + */ + public ByteArrayAnnotatedOutput(int size) { + this(new byte[size], true); + } + + /** + * Internal constructor. + * + * @param data {@code 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"); } - /** - * Constructs a "stretchy" instance. The underlying array may be - * reallocated. The constructed instance does not keep annotations - * by default. - */ - public ByteArrayAnnotatedOutput() { - this(DEFAULT_SIZE); + 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[]} of this instance, which + * may be larger than the number of bytes written + * + * @see #toByteArray + * + * @return {@code non-null;} the {@code byte[]} + */ + public byte[] getArray() { + return data; + } + + /** + * Constructs and returns a new {@code byte[]} that contains + * the written contents exactly (that is, with no extra unwritten + * bytes at the end). + * + * @see #getArray + * + * @return {@code non-null;} an appropriately-constructed array + */ + public byte[] toByteArray() { + byte[] result = new byte[cursor]; + System.arraycopy(data, 0, result, 0, cursor); + return result; + } + + /** {@inheritDoc} */ + @Override + public int getCursor() { + return cursor; + } + + /** {@inheritDoc} */ + @Override + public void assertCursor(int expectedCursor) { + if (cursor != expectedCursor) { + throw new ExceptionWithContext( + "expected cursor " + expectedCursor + "; actual value: " + cursor); } - - /** - * Constructs a "stretchy" instance with initial size {@code size}. The - * underlying array may be reallocated. The constructed instance does not - * keep annotations by default. - */ - public ByteArrayAnnotatedOutput(int size) { - this(new byte[size], true); + } + + /** {@inheritDoc} */ + @Override + public void writeByte(int value) { + int writeAt = cursor; + int end = writeAt + 1; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; } - /** - * Internal constructor. - * - * @param data {@code 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; + data[writeAt] = (byte) value; + cursor = end; + } + + /** {@inheritDoc} */ + @Override + public void writeShort(int value) { + int writeAt = cursor; + int end = writeAt + 2; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; } - /** - * Gets the underlying {@code byte[]} of this instance, which - * may be larger than the number of bytes written - * - * @see #toByteArray - * - * @return {@code non-null;} the {@code byte[]} - */ - public byte[] getArray() { - return data; + data[writeAt] = (byte) value; + data[writeAt + 1] = (byte) (value >> 8); + cursor = end; + } + + /** {@inheritDoc} */ + @Override + public void writeInt(int value) { + int writeAt = cursor; + int end = writeAt + 4; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; } - /** - * Constructs and returns a new {@code byte[]} that contains - * the written contents exactly (that is, with no extra unwritten - * bytes at the end). - * - * @see #getArray - * - * @return {@code non-null;} an appropriately-constructed array - */ - public byte[] toByteArray() { - byte[] result = new byte[cursor]; - System.arraycopy(data, 0, result, 0, cursor); - return result; + 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} */ + @Override + public void writeLong(long value) { + int writeAt = cursor; + int end = writeAt + 8; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; } - /** {@inheritDoc} */ - public int getCursor() { - return cursor; + 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} */ + @Override + public int writeUleb128(int value) { + if (stretchy) { + ensureCapacity(cursor + 5); // pessimistic } - - /** {@inheritDoc} */ - public void assertCursor(int expectedCursor) { - if (cursor != expectedCursor) { - throw new ExceptionWithContext("expected cursor " + - expectedCursor + "; actual value: " + cursor); - } + int cursorBefore = cursor; + Leb128Utils.writeUnsignedLeb128(this, value); + return (cursor - cursorBefore); + } + + /** {@inheritDoc} */ + @Override + public int writeSleb128(int value) { + if (stretchy) { + ensureCapacity(cursor + 5); // pessimistic } - - /** {@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; + int cursorBefore = cursor; + Leb128Utils.writeSignedLeb128(this, value); + return (cursor - cursorBefore); + } + + /** {@inheritDoc} */ + @Override + 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; } - /** {@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; + bytes.getBytes(data, writeAt); + cursor = end; + } + + /** {@inheritDoc} */ + @Override + 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); } - /** {@inheritDoc} */ - public void writeInt(int value) { - int writeAt = cursor; - int end = writeAt + 4; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } + 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; + System.arraycopy(bytes, offset, data, writeAt, length); + cursor = end; + } + + /** {@inheritDoc} */ + @Override + public void write(byte[] bytes) { + write(bytes, 0, bytes.length); + } + + /** {@inheritDoc} */ + @Override + public void writeZeroes(int count) { + if (count < 0) { + throw new IllegalArgumentException("count < 0"); } - /** {@inheritDoc} */ - public void writeLong(long value) { - int writeAt = cursor; - int end = writeAt + 8; + int end = cursor + count; - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } + 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); + /* + * There is no need to actually write zeroes, since the array is + * already preinitialized with zeroes. + */ - 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; + } - cursor = end; - } + /** {@inheritDoc} */ + @Override + public void alignTo(int alignment) { + int mask = alignment - 1; - /** {@inheritDoc} */ - public int writeUleb128(int value) { - if (stretchy) { - ensureCapacity(cursor + 5); // pessimistic - } - int cursorBefore = cursor; - Leb128Utils.writeUnsignedLeb128(this, value); - return (cursor - cursorBefore); + if ((alignment < 0) || ((mask & alignment) != 0)) { + throw new IllegalArgumentException("bogus alignment"); } - /** {@inheritDoc} */ - public int writeSleb128(int value) { - if (stretchy) { - ensureCapacity(cursor + 5); // pessimistic - } - int cursorBefore = cursor; - Leb128Utils.writeSignedLeb128(this, value); - return (cursor - cursorBefore); - } - - /** {@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; - } + int end = (cursor + mask) & ~mask; - bytes.getBytes(data, writeAt); - cursor = end; + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; } - /** {@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; - } + /* + * There is no need to actually write zeroes, since the array is + * already preinitialized with zeroes. + */ - /** {@inheritDoc} */ - public void write(byte[] bytes) { - write(bytes, 0, bytes.length); +cursor = end; + } + + /** {@inheritDoc} */ + @Override + public boolean annotates() { + return (annotations != null); + } + + /** {@inheritDoc} */ + @Override + public boolean isVerbose() { + return verbose; + } + + /** {@inheritDoc} */ + @Override + public void annotate(String msg) { + if (annotations == null) { + return; } - /** {@inheritDoc} */ - public void writeZeroes(int count) { - if (count < 0) { - throw new IllegalArgumentException("count < 0"); - } + endAnnotation(); + annotations.add(new Annotation(cursor, msg)); + } - int end = cursor + count; + /** {@inheritDoc} */ + @Override + public void annotate(int amt, String msg) { + if (annotations == null) { + return; + } - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } + endAnnotation(); - /* - * There is no need to actually write zeroes, since the array is - * already preinitialized with zeroes. - */ + int asz = annotations.size(); + int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd(); + int startAt; - cursor = end; + if (lastEnd <= cursor) { + startAt = cursor; + } else { + startAt = lastEnd; } - /** {@inheritDoc} */ - public void alignTo(int alignment) { - int mask = alignment - 1; + annotations.add(new Annotation(startAt, startAt + amt, msg)); + } - 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; - } + /** {@inheritDoc} */ + @Override + public void endAnnotation() { + if (annotations == null) { + return; + } - /* - * There is no need to actually write zeroes, since the array is - * already preinitialized with zeroes. - */ + int sz = annotations.size(); - cursor = end; + if (sz != 0) { + annotations.get(sz - 1).setEndIfUnset(cursor); } - - /** {@inheritDoc} */ - public boolean annotates() { - return (annotations != null); + } + + /** {@inheritDoc} */ + @Override + 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 {@code >= 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"); } - /** {@inheritDoc} */ - public boolean isVerbose() { - return verbose; + if (annotationWidth < 40) { + throw new IllegalArgumentException("annotationWidth < 40"); } - /** {@inheritDoc} */ - public void annotate(String msg) { - if (annotations == null) { - return; - } - - endAnnotation(); - annotations.add(new Annotation(cursor, msg)); + int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1; + if (hexCols < 6) { + hexCols = 6; + } else if (hexCols > 10) { + hexCols = 10; } - /** {@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; + 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 { - startAt = lastEnd; + break; } - - annotations.add(new Annotation(startAt, startAt + amt, msg)); + } + } + } + + /** + * Writes the annotated content of this instance to the given writer. + * + * @param out {@code non-null;} where to write to + */ + public void writeAnnotationsTo(Writer out) throws IOException { + int width2 = getAnnotationWidth(); + int width1 = annotationWidth - width2 - 1; + + TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|"); + Writer left = twoc.getLeft(); + Writer right = twoc.getRight(); + int leftAt = 0; // left-hand byte output cursor + int rightAt = 0; // right-hand annotation index + int rightSz = annotations.size(); + + while ((leftAt < cursor) && (rightAt < rightSz)) { + Annotation a = annotations.get(rightAt); + int start = a.getStart(); + int end; + String text; + + if (leftAt < start) { + // This is an area with no annotation. + end = start; + start = leftAt; + text = ""; + } else { + // This is an area with an annotation. + end = a.getEnd(); + text = a.getText(); + rightAt++; + } + + left.write(Hex.dump(data, start, end - start, start, hexCols, 6)); + right.write(text); + twoc.flush(); + leftAt = end; } - /** {@inheritDoc} */ - public void endAnnotation() { - if (annotations == null) { - return; - } - - int sz = annotations.size(); - - if (sz != 0) { - annotations.get(sz - 1).setEndIfUnset(cursor); - } + if (leftAt < cursor) { + // There is unannotated output at the end. + left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt, hexCols, 6)); } - /** {@inheritDoc} */ - public int getAnnotationWidth() { - int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); + while (rightAt < rightSz) { + // There are zero-byte annotations at the end. + right.write(annotations.get(rightAt).getText()); + rightAt++; + } - return annotationWidth - leftWidth; + 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 {@code >= 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 { + /** {@code >= 0;} start of annotated range (inclusive) */ + private final int start; /** - * 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 {@code >= 40;} the desired maximum annotation width - * @param verbose whether or not to indicate verbose annotations + * {@code >= 0;} end of annotated range (exclusive); + * {@code Integer.MAX_VALUE} if unclosed */ - public void enableAnnotations(int annotationWidth, boolean verbose) { - if ((annotations != null) || (cursor != 0)) { - throw new RuntimeException("cannot enable annotations"); - } + private int end; - if (annotationWidth < 40) { - throw new IllegalArgumentException("annotationWidth < 40"); - } + /** {@code non-null;} annotation text */ + private final String text; - 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; + /** + * Constructs an instance. + * + * @param start {@code >= 0;} start of annotated range + * @param end {@code >= start;} end of annotated range (exclusive) or + * {@code Integer.MAX_VALUE} if unclosed + * @param text {@code non-null;} annotation text + */ + public Annotation(int start, int end, String text) { + this.start = start; + this.end = end; + this.text = text; } /** - * Finishes up annotation processing. This closes off any open - * annotations and removes annotations that don't refer to written - * data. + * Constructs an instance. It is initally unclosed. + * + * @param start {@code >= 0;} start of annotated range + * @param text {@code non-null;} annotation text */ - 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; - } - } - } + public Annotation(int start, String text) { + this(start, Integer.MAX_VALUE, text); } /** - * Writes the annotated content of this instance to the given writer. + * Sets the end as given, but only if the instance is unclosed; + * otherwise, do nothing. * - * @param out {@code non-null;} where to write to + * @param end {@code >= start;} the end */ - public void writeAnnotationsTo(Writer out) throws IOException { - int width2 = getAnnotationWidth(); - int width1 = annotationWidth - width2 - 1; - - TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|"); - Writer left = twoc.getLeft(); - Writer right = twoc.getRight(); - int leftAt = 0; // left-hand byte output cursor - int rightAt = 0; // right-hand annotation index - int rightSz = annotations.size(); - - while ((leftAt < cursor) && (rightAt < rightSz)) { - Annotation a = annotations.get(rightAt); - int start = a.getStart(); - int end; - String text; - - if (leftAt < start) { - // This is an area with no annotation. - end = start; - start = leftAt; - text = ""; - } else { - // This is an area with an annotation. - end = a.getEnd(); - text = a.getText(); - rightAt++; - } - - left.write(Hex.dump(data, start, end - start, start, hexCols, 6)); - right.write(text); - twoc.flush(); - leftAt = end; - } - - if (leftAt < cursor) { - // There is unannotated output at the end. - left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt, - hexCols, 6)); - } - - while (rightAt < rightSz) { - // There are zero-byte annotations at the end. - right.write(annotations.get(rightAt).getText()); - rightAt++; - } - - twoc.flush(); + public void setEndIfUnset(int end) { + if (this.end == Integer.MAX_VALUE) { + this.end = end; + } } /** - * Throws the excpetion for when an attempt is made to write past the - * end of the instance. + * Sets the end as given. + * + * @param end {@code >= start;} the end */ - private static void throwBounds() { - throw new IndexOutOfBoundsException("attempt to write past the end"); + public void setEnd(int end) { + this.end = end; } /** - * Reallocates the underlying array if necessary. Calls to this method - * should be guarded by a test of {@link #stretchy}. + * Gets the start. * - * @param desiredSize {@code >= 0;} the desired minimum total size of the array + * @return the start */ - 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; - } + public int getStart() { + return start; } /** - * Annotation on output. + * Gets the end. + * + * @return the end */ - private static class Annotation { - /** {@code >= 0;} start of annotated range (inclusive) */ - private final int start; - - /** - * {@code >= 0;} end of annotated range (exclusive); - * {@code Integer.MAX_VALUE} if unclosed - */ - private int end; - - /** {@code non-null;} annotation text */ - private final String text; - - /** - * Constructs an instance. - * - * @param start {@code >= 0;} start of annotated range - * @param end {@code >= start;} end of annotated range (exclusive) or - * {@code Integer.MAX_VALUE} if unclosed - * @param text {@code 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 {@code >= 0;} start of annotated range - * @param text {@code 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 {@code >= start;} the end - */ - public void setEndIfUnset(int end) { - if (this.end == Integer.MAX_VALUE) { - this.end = end; - } - } - - /** - * Sets the end as given. - * - * @param end {@code >= 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; - } + public int getEnd() { + return end; + } - /** - * Gets the text. - * - * @return {@code non-null;} the text - */ - public String getText() { - return text; - } + /** + * Gets the text. + * + * @return {@code non-null;} the text + */ + public String getText() { + return text; } + } } diff --git a/dx/src/com/android/jack/dx/util/ByteArrayByteInput.java b/dx/src/com/android/jack/dx/util/ByteArrayByteInput.java index 8908997..4d7c7ba 100644 --- a/dx/src/com/android/jack/dx/util/ByteArrayByteInput.java +++ b/dx/src/com/android/jack/dx/util/ByteArrayByteInput.java @@ -16,16 +16,20 @@ package com.android.jack.dx.util; +/** + * TODO(jack team) + */ public final class ByteArrayByteInput implements ByteInput { - private final byte[] bytes; - private int position; + private final byte[] bytes; + private int position; - public ByteArrayByteInput(byte... bytes) { - this.bytes = bytes; - } + public ByteArrayByteInput(byte... bytes) { + this.bytes = bytes; + } - @Override public byte readByte() { - return bytes[position++]; - } + @Override + public byte readByte() { + return bytes[position++]; + } } diff --git a/dx/src/com/android/jack/dx/util/ByteInput.java b/dx/src/com/android/jack/dx/util/ByteInput.java index 00246e6..7abba45 100644 --- a/dx/src/com/android/jack/dx/util/ByteInput.java +++ b/dx/src/com/android/jack/dx/util/ByteInput.java @@ -21,10 +21,10 @@ package com.android.jack.dx.util; */ public interface ByteInput { - /** - * Returns a byte. - * - * @throws IndexOutOfBoundsException if all bytes have been read. - */ - byte readByte(); + /** + * Returns a byte. + * + * @throws IndexOutOfBoundsException if all bytes have been read. + */ + byte readByte(); } diff --git a/dx/src/com/android/jack/dx/util/ByteOutput.java b/dx/src/com/android/jack/dx/util/ByteOutput.java index 1a2b8f4..7ff9457 100644 --- a/dx/src/com/android/jack/dx/util/ByteOutput.java +++ b/dx/src/com/android/jack/dx/util/ByteOutput.java @@ -21,10 +21,10 @@ package com.android.jack.dx.util; */ public interface ByteOutput { - /** - * Writes a byte. - * - * @throws IndexOutOfBoundsException if all bytes have been written. - */ - void writeByte(int i); + /** + * Writes a byte. + * + * @throws IndexOutOfBoundsException if all bytes have been written. + */ + void writeByte(int i); } diff --git a/dx/src/com/android/jack/dx/util/DexException.java b/dx/src/com/android/jack/dx/util/DexException.java index c06c9f3..d03b92c 100644 --- a/dx/src/com/android/jack/dx/util/DexException.java +++ b/dx/src/com/android/jack/dx/util/DexException.java @@ -21,11 +21,14 @@ package com.android.jack.dx.util; * processing a dex file. */ public final class DexException extends ExceptionWithContext { - public DexException(String message) { - super(message); - } - public DexException(Throwable cause) { - super(cause); - } + private static final long serialVersionUID = 1L; + + public DexException(String message) { + super(message); + } + + public DexException(Throwable cause) { + super(cause); + } } diff --git a/dx/src/com/android/jack/dx/util/ExceptionWithContext.java b/dx/src/com/android/jack/dx/util/ExceptionWithContext.java index 8951eaf..028e917 100644 --- a/dx/src/com/android/jack/dx/util/ExceptionWithContext.java +++ b/dx/src/com/android/jack/dx/util/ExceptionWithContext.java @@ -22,128 +22,128 @@ import java.io.PrintWriter; /** * Exception which carries around structured context. */ -public class ExceptionWithContext - extends RuntimeException { - /** {@code 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 {@code non-null;} the exception to augment - * @param str {@code non-null;} context to add - * @return {@code 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; +public class ExceptionWithContext extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** {@code 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 {@code non-null;} the exception to augment + * @param str {@code non-null;} context to add + * @return {@code 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); } - /** - * Constructs an instance. - * - * @param message human-oriented message - */ - public ExceptionWithContext(String message) { - this(message, null); + 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 {@code null-ok;} exception that caused this one + */ + public ExceptionWithContext(Throwable cause) { + this(null, cause); + } + + /** + * Constructs an instance. + * + * @param message human-oriented message + * @param cause {@code 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); } - - /** - * Constructs an instance. - * - * @param cause {@code null-ok;} exception that caused this one - */ - public ExceptionWithContext(Throwable cause) { - this(null, cause); - } - - /** - * Constructs an instance. - * - * @param message human-oriented message - * @param cause {@code 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 {@code 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 {@code non-null;} the context - */ - public String getContext() { - return context.toString(); - } - - /** - * Prints the message and context. - * - * @param out {@code non-null;} where to print to - */ - public void printContext(PrintStream out) { - out.println(getMessage()); - out.print(context); + } + + /** {@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 {@code non-null;} new context + */ + public void addContext(String str) { + if (str == null) { + throw new NullPointerException("str == null"); } - /** - * Prints the message and context. - * - * @param out {@code non-null;} where to print to - */ - public void printContext(PrintWriter out) { - out.println(getMessage()); - out.print(context); + context.append(str); + if (!str.endsWith("\n")) { + context.append('\n'); } + } + + /** + * Gets the context. + * + * @return {@code non-null;} the context + */ + public String getContext() { + return context.toString(); + } + + /** + * Prints the message and context. + * + * @param out {@code non-null;} where to print to + */ + public void printContext(PrintStream out) { + out.println(getMessage()); + out.print(context); + } + + /** + * Prints the message and context. + * + * @param out {@code non-null;} where to print to + */ + public void printContext(PrintWriter out) { + out.println(getMessage()); + out.print(context); + } } diff --git a/dx/src/com/android/jack/dx/util/FileUtils.java b/dx/src/com/android/jack/dx/util/FileUtils.java index 4c71d52..061f27e 100644 --- a/dx/src/com/android/jack/dx/util/FileUtils.java +++ b/dx/src/com/android/jack/dx/util/FileUtils.java @@ -24,78 +24,84 @@ import java.io.IOException; * File I/O utilities. */ public final class FileUtils { - /** - * This class is uninstantiable. - */ - private FileUtils() { - // This space intentionally left blank. + /** + * 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 {@code non-null;} name of the file to read + * @return {@code non-null;} contents of the file + */ + public static byte[] readFile(String fileName) { + File file = new File(fileName); + return readFile(file); + } + + /** + * Reads the given file, translating {@link IOException} to a + * {@link RuntimeException} of some sort. + * + * @param file {@code non-null;} the file to read + * @return {@code non-null;} contents of the file + */ + public static byte[] readFile(File file) { + if (!file.exists()) { + throw new RuntimeException(file + ": file not found"); } - /** - * Reads the named file, translating {@link IOException} to a - * {@link RuntimeException} of some sort. - * - * @param fileName {@code non-null;} name of the file to read - * @return {@code non-null;} contents of the file - */ - public static byte[] readFile(String fileName) { - File file = new File(fileName); - return readFile(file); + if (!file.isFile()) { + throw new RuntimeException(file + ": not a file"); } - /** - * Reads the given file, translating {@link IOException} to a - * {@link RuntimeException} of some sort. - * - * @param file {@code non-null;} the file to read - * @return {@code non-null;} contents of the file - */ - public static byte[] readFile(File file) { - if (!file.exists()) { - throw new RuntimeException(file + ": file not found"); - } + if (!file.canRead()) { + throw new RuntimeException(file + ": file not readable"); + } - if (!file.isFile()) { - throw new RuntimeException(file + ": not a file"); - } + long longLength = file.length(); + int length = (int) longLength; + if (length != longLength) { + throw new RuntimeException(file + ": file too long"); + } - if (!file.canRead()) { - throw new RuntimeException(file + ": file not readable"); - } + byte[] result = new byte[length]; - long longLength = file.length(); - int length = (int) longLength; - if (length != longLength) { - throw new RuntimeException(file + ": file too long"); + FileInputStream in = null; + try { + in = new FileInputStream(file); + int at = 0; + while (length > 0) { + int amt = in.read(result, at, length); + if (amt == -1) { + throw new RuntimeException(file + ": unexpected EOF"); } - - byte[] result = new byte[length]; - + at += amt; + length -= amt; + } + } catch (IOException ex) { + throw new RuntimeException(file + ": trouble reading", ex); + } finally { + if (in != null) { try { - FileInputStream in = new FileInputStream(file); - int at = 0; - while (length > 0) { - int amt = in.read(result, at, length); - if (amt == -1) { - throw new RuntimeException(file + ": unexpected EOF"); - } - at += amt; - length -= amt; - } - in.close(); + in.close(); } catch (IOException ex) { - throw new RuntimeException(file + ": trouble reading", ex); + throw new RuntimeException(file + ": trouble reading", ex); } - - return result; + } } - /** - * Returns true if {@code fileName} names a .zip, .jar, or .apk. - */ - public static boolean hasArchiveSuffix(String fileName) { - return fileName.endsWith(".zip") - || fileName.endsWith(".jar") - || fileName.endsWith(".apk"); - } + return result; + } + + /** + * Returns true if {@code fileName} names a .zip, .jar, or .apk. + */ + public static boolean hasArchiveSuffix(String fileName) { + return fileName.endsWith(".zip") || fileName.endsWith(".jar") || fileName.endsWith(".apk"); + } } diff --git a/dx/src/com/android/jack/dx/util/FixedSizeList.java b/dx/src/com/android/jack/dx/util/FixedSizeList.java index a8badf0..9463877 100644 --- a/dx/src/com/android/jack/dx/util/FixedSizeList.java +++ b/dx/src/com/android/jack/dx/util/FixedSizeList.java @@ -21,256 +21,249 @@ import java.util.Arrays; /** * Simple (mostly) fixed-size list of objects, which may be made immutable. */ -public class FixedSizeList - extends MutabilityControl implements ToHuman { - /** {@code non-null;} array of elements */ - private Object[] arr; - - /** - * Constructs an instance. All indices initially contain {@code null}. - * - * @param size the size of the list - */ - public FixedSizeList(int size) { - super(size != 0); - - try { - arr = new Object[size]; - } catch (NegativeArraySizeException ex) { - // Translate the exception. - throw new IllegalArgumentException("size < 0"); - } +public class FixedSizeList extends MutabilityControl implements ToHuman { + /** {@code non-null;} array of elements */ + private Object[] arr; + + /** + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size the size of the list + */ + public FixedSizeList(int size) { + super(size != 0); + + try { + arr = new Object[size]; + } catch (NegativeArraySizeException ex) { + // Translate the exception. + throw new IllegalArgumentException("size < 0"); } - - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (this == other) { - // Easy out. - return true; - } - - if ((other == null) || (getClass() != other.getClass())) { - // Another easy out. - return false; - } - - FixedSizeList list = (FixedSizeList) other; - return Arrays.equals(arr, list.arr); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (this == other) { + // Easy out. + return true; } - /** {@inheritDoc} */ - @Override - public int hashCode() { - return Arrays.hashCode(arr); + if ((other == null) || (getClass() != other.getClass())) { + // Another easy out. + return false; } - /** {@inheritDoc} */ - @Override - public String toString() { - String name = getClass().getName(); - - return toString0(name.substring(name.lastIndexOf('.') + 1) + '{', - ", ", - "}", - false); + FixedSizeList list = (FixedSizeList) other; + return Arrays.equals(arr, list.arr); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return Arrays.hashCode(arr); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + String name = getClass().getName(); + + return toString0(name.substring(name.lastIndexOf('.') + 1) + '{', ", ", "}", false); + } + + /** + * {@inheritDoc} + * + * This method will only work if every element of the list + * implements {@link ToHuman}. + */ + @Override + public String toHuman() { + String name = getClass().getName(); + + return toString0(name.substring(name.lastIndexOf('.') + 1) + '{', ", ", "}", true); + } + + /** + * Gets a customized string form for this instance. + * + * @param prefix {@code null-ok;} prefix for the start of the result + * @param separator {@code null-ok;} separator to insert between each item + * @param suffix {@code null-ok;} suffix for the end of the result + * @return {@code non-null;} the custom string + */ + public String toString(String prefix, String separator, String suffix) { + return toString0(prefix, separator, suffix, false); + } + + /** + * Gets a customized human string for this instance. This method will + * only work if every element of the list implements {@link + * ToHuman}. + * + * @param prefix {@code null-ok;} prefix for the start of the result + * @param separator {@code null-ok;} separator to insert between each item + * @param suffix {@code null-ok;} suffix for the end of the result + * @return {@code non-null;} the custom string + */ + public String toHuman(String prefix, String separator, String suffix) { + return toString0(prefix, separator, suffix, true); + } + + /** + * Gets the number of elements in this list. + */ + public final int size() { + return arr.length; + } + + /** + * Shrinks this instance to fit, by removing any unset + * ({@code null}) elements, leaving the remaining elements in + * their original order. + */ + public void shrinkToFit() { + int sz = arr.length; + int newSz = 0; + + for (int i = 0; i < sz; i++) { + if (arr[i] != null) { + newSz++; + } } - /** - * {@inheritDoc} - * - * This method will only work if every element of the list - * implements {@link ToHuman}. - */ - public String toHuman() { - String name = getClass().getName(); - - return toString0(name.substring(name.lastIndexOf('.') + 1) + '{', - ", ", - "}", - true); + if (sz == newSz) { + return; } - /** - * Gets a customized string form for this instance. - * - * @param prefix {@code null-ok;} prefix for the start of the result - * @param separator {@code null-ok;} separator to insert between each item - * @param suffix {@code null-ok;} suffix for the end of the result - * @return {@code non-null;} the custom string - */ - public String toString(String prefix, String separator, String suffix) { - return toString0(prefix, separator, suffix, false); - } + throwIfImmutable(); - /** - * Gets a customized human string for this instance. This method will - * only work if every element of the list implements {@link - * ToHuman}. - * - * @param prefix {@code null-ok;} prefix for the start of the result - * @param separator {@code null-ok;} separator to insert between each item - * @param suffix {@code null-ok;} suffix for the end of the result - * @return {@code non-null;} the custom string - */ - public String toHuman(String prefix, String separator, String suffix) { - return toString0(prefix, separator, suffix, true); - } + Object[] newa = new Object[newSz]; + int at = 0; - /** - * Gets the number of elements in this list. - */ - public final int size() { - return arr.length; + for (int i = 0; i < sz; i++) { + Object one = arr[i]; + if (one != null) { + newa[at] = one; + at++; + } } - /** - * Shrinks this instance to fit, by removing any unset - * ({@code null}) elements, leaving the remaining elements in - * their original order. - */ - public void shrinkToFit() { - int sz = arr.length; - int newSz = 0; - - for (int i = 0; i < sz; i++) { - if (arr[i] != null) { - newSz++; - } - } - - if (sz == newSz) { - return; - } - - throwIfImmutable(); - - Object[] newa = new Object[newSz]; - int at = 0; - - for (int i = 0; i < sz; i++) { - Object one = arr[i]; - if (one != null) { - newa[at] = one; - at++; - } - } - - arr = newa; - if (newSz == 0) { - setImmutable(); - } + arr = newa; + if (newSz == 0) { + setImmutable(); } - - /** - * Gets the indicated element. It is an error to call this with the - * index for an element which was never set; if you do that, this - * will throw {@code NullPointerException}. This method is - * protected so that subclasses may offer a safe type-checked - * public interface to their clients. - * - * @param n {@code >= 0, < size();} which element - * @return {@code non-null;} the indicated element - */ - protected final Object get0(int n) { - try { - Object result = arr[n]; - - if (result == null) { - throw new NullPointerException("unset: " + n); - } - - return result; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - return throwIndex(n); - } + } + + /** + * Gets the indicated element. It is an error to call this with the + * index for an element which was never set; if you do that, this + * will throw {@code NullPointerException}. This method is + * protected so that subclasses may offer a safe type-checked + * public interface to their clients. + * + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element + */ + protected final Object get0(int n) { + try { + Object result = arr[n]; + + if (result == null) { + throw new NullPointerException("unset: " + n); + } + + return result; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + return throwIndex(n); } - - /** - * Gets the indicated element, allowing {@code null}s to be - * returned. This method is protected so that subclasses may - * (optionally) offer a safe type-checked public interface to - * their clients. - * - * @param n {@code >= 0, < size();} which element - * @return {@code null-ok;} the indicated element - */ - protected final Object getOrNull0(int n) { - return arr[n]; + } + + /** + * Gets the indicated element, allowing {@code null}s to be + * returned. This method is protected so that subclasses may + * (optionally) offer a safe type-checked public interface to + * their clients. + * + * @param n {@code >= 0, < size();} which element + * @return {@code null-ok;} the indicated element + */ + protected final Object getOrNull0(int n) { + return arr[n]; + } + + /** + * Sets the element at the given index, but without doing any type + * checks on the element. This method is protected so that + * subclasses may offer a safe type-checked public interface to + * their clients. + * + * @param n {@code >= 0, < size();} which element + * @param obj {@code null-ok;} the value to store + */ + protected final void set0(int n, Object obj) { + throwIfImmutable(); + + try { + arr[n] = obj; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + throwIndex(n); + } + } + + /** + * Throws the appropriate exception for the given index value. + * + * @param n the index value + * @return never + * @throws IndexOutOfBoundsException always thrown + */ + private Object throwIndex(int n) { + if (n < 0) { + throw new IndexOutOfBoundsException("n < 0"); } - /** - * Sets the element at the given index, but without doing any type - * checks on the element. This method is protected so that - * subclasses may offer a safe type-checked public interface to - * their clients. - * - * @param n {@code >= 0, < size();} which element - * @param obj {@code null-ok;} the value to store - */ - protected final void set0(int n, Object obj) { - throwIfImmutable(); - - try { - arr[n] = obj; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - throwIndex(n); - } + throw new IndexOutOfBoundsException("n >= size()"); + } + + /** + * Helper for {@link #toString} and {@link #toHuman}, which both of + * those call to pretty much do everything. + * + * @param prefix {@code null-ok;} prefix for the start of the result + * @param separator {@code null-ok;} separator to insert between each item + * @param suffix {@code null-ok;} suffix for the end of the result + * @param human whether the output is to be human + * @return {@code non-null;} the custom string + */ + private String toString0(String prefix, String separator, String suffix, boolean human) { + int len = arr.length; + StringBuffer sb = new StringBuffer(len * 10 + 10); + + if (prefix != null) { + sb.append(prefix); } - /** - * Throws the appropriate exception for the given index value. - * - * @param n the index value - * @return never - * @throws IndexOutOfBoundsException always thrown - */ - private Object throwIndex(int n) { - if (n < 0) { - throw new IndexOutOfBoundsException("n < 0"); - } - - throw new IndexOutOfBoundsException("n >= size()"); + for (int i = 0; i < len; i++) { + if ((i != 0) && (separator != null)) { + sb.append(separator); + } + + if (human) { + sb.append(((ToHuman) arr[i]).toHuman()); + } else { + sb.append(arr[i]); + } } - /** - * Helper for {@link #toString} and {@link #toHuman}, which both of - * those call to pretty much do everything. - * - * @param prefix {@code null-ok;} prefix for the start of the result - * @param separator {@code null-ok;} separator to insert between each item - * @param suffix {@code null-ok;} suffix for the end of the result - * @param human whether the output is to be human - * @return {@code non-null;} the custom string - */ - private String toString0(String prefix, String separator, String suffix, - boolean human) { - int len = arr.length; - StringBuffer sb = new StringBuffer(len * 10 + 10); - - if (prefix != null) { - sb.append(prefix); - } - - for (int i = 0; i < len; i++) { - if ((i != 0) && (separator != null)) { - sb.append(separator); - } - - if (human) { - sb.append(((ToHuman) arr[i]).toHuman()); - } else { - sb.append(arr[i]); - } - } - - if (suffix != null) { - sb.append(suffix); - } - - return sb.toString(); + if (suffix != null) { + sb.append(suffix); } + return sb.toString(); + } + } diff --git a/dx/src/com/android/jack/dx/util/Hex.java b/dx/src/com/android/jack/dx/util/Hex.java index e928265..740c03c 100644 --- a/dx/src/com/android/jack/dx/util/Hex.java +++ b/dx/src/com/android/jack/dx/util/Hex.java @@ -20,284 +20,293 @@ package com.android.jack.dx.util; * Utilities for formatting numbers as hexadecimal. */ public final class Hex { - /** - * This class is uninstantiable. - */ - private Hex() { - // This space intentionally left blank. + /** + * This class is uninstantiable. + */ + private Hex() { + // This space intentionally left blank. + } + + /** + * Formats a {@code long} as an 8-byte unsigned hex value. + * + * @param v value to format + * @return {@code 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; } - /** - * Formats a {@code long} as an 8-byte unsigned hex value. - * - * @param v value to format - * @return {@code 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); + return new String(result); + } + + /** + * Formats an {@code int} as a 4-byte unsigned hex value. + * + * @param v value to format + * @return {@code 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; } - /** - * Formats an {@code int} as a 4-byte unsigned hex value. - * - * @param v value to format - * @return {@code 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); + return new String(result); + } + + /** + * Formats an {@code int} as a 3-byte unsigned hex value. + * + * @param v value to format + * @return {@code 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; } - /** - * Formats an {@code int} as a 3-byte unsigned hex value. - * - * @param v value to format - * @return {@code 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); + return new String(result); + } + + /** + * Formats an {@code int} as a 2-byte unsigned hex value. + * + * @param v value to format + * @return {@code 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; } - /** - * Formats an {@code int} as a 2-byte unsigned hex value. - * - * @param v value to format - * @return {@code 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); + return new String(result); + } + + /** + * Formats an {@code int} 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 {@code 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} 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 {@code 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} as a 1-byte unsigned hex value. + * + * @param v value to format + * @return {@code 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; } - /** - * Formats an {@code int} as a 1-byte unsigned hex value. - * - * @param v value to format - * @return {@code 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); + return new String(result); + } + + /** + * Formats an {@code int} as a 4-bit unsigned hex nibble. + * + * @param v value to format + * @return {@code 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} as an 8-byte signed hex value. + * + * @param v value to format + * @return {@code 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] = '+'; } - /** - * Formats an {@code int} as a 4-bit unsigned hex nibble. - * - * @param v value to format - * @return {@code 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); + for (int i = 0; i < 16; i++) { + result[16 - i] = Character.forDigit((int) v & 0x0f, 16); + v >>= 4; } - /** - * Formats a {@code long} as an 8-byte signed hex value. - * - * @param v value to format - * @return {@code 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); + return new String(result); + } + + /** + * Formats an {@code int} as a 4-byte signed hex value. + * + * @param v value to format + * @return {@code 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] = '+'; } - /** - * Formats an {@code int} as a 4-byte signed hex value. - * - * @param v value to format - * @return {@code 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); + for (int i = 0; i < 8; i++) { + result[8 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; } - /** - * Formats an {@code int} as a 2-byte signed hex value. - * - * @param v value to format - * @return {@code 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); + return new String(result); + } + + /** + * Formats an {@code int} as a 2-byte signed hex value. + * + * @param v value to format + * @return {@code 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] = '+'; } - /** - * Formats an {@code int} as a 1-byte signed hex value. - * - * @param v value to format - * @return {@code 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; - } + for (int i = 0; i < 4; i++) { + result[4 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } - return new String(result); + return new String(result); + } + + /** + * Formats an {@code int} as a 1-byte signed hex value. + * + * @param v value to format + * @return {@code 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] = '+'; } - /** - * Formats a hex dump of a portion of a {@code byte[]}. The result - * is always newline-terminated, unless the passed-in length was zero, - * in which case the result is always the empty string ({@code ""}). - * - * @param arr {@code non-null;} array to format - * @param offset {@code >= 0;} offset to the part to dump - * @param length {@code >= 0;} number of bytes to dump - * @param outOffset {@code >= 0;} first output offset to print - * @param bpl {@code >= 0;} number of bytes of output per line - * @param addressLength {@code {2,4,6,8};} number of characters for each address - * header - * @return {@code 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); - } + for (int i = 0; i < 2; i++) { + result[2 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } - if (outOffset < 0) { - throw new IllegalArgumentException("outOffset < 0"); - } + return new String(result); + } + + /** + * Formats a hex dump of a portion of a {@code byte[]}. The result + * is always newline-terminated, unless the passed-in length was zero, + * in which case the result is always the empty string ({@code ""}). + * + * @param arr {@code non-null;} array to format + * @param offset {@code >= 0;} offset to the part to dump + * @param length {@code >= 0;} number of bytes to dump + * @param outOffset {@code >= 0;} first output offset to print + * @param bpl {@code >= 0;} number of bytes of output per line + * @param addressLength {@code {2,4,6,8};} number of characters for each address + * header + * @return {@code 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 (length == 0) { - return ""; - } + if (outOffset < 0) { + throw new IllegalArgumentException("outOffset < 0"); + } - 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 (length == 0) { + return ""; + } - if (col != 0) { - sb.append('\n'); + StringBuffer sb = new StringBuffer(length * 4 + 6); + 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--; + } - return sb.toString(); + if (col != 0) { + sb.append('\n'); } + + return sb.toString(); + } } diff --git a/dx/src/com/android/jack/dx/util/HexParser.java b/dx/src/com/android/jack/dx/util/HexParser.java index 551180a..71ed283 100644 --- a/dx/src/com/android/jack/dx/util/HexParser.java +++ b/dx/src/com/android/jack/dx/util/HexParser.java @@ -20,126 +20,120 @@ package com.android.jack.dx.util; * Utilities for parsing hexadecimal text. */ public final class HexParser { - /** - * This class is uninstantiable. - */ - private HexParser() { - // This space intentionally left blank. - } + /** + * This class is uninstantiable. + */ + private HexParser() { + // This space intentionally left blank. + } + + /** + * Parses the given text as hex, returning a {@code byte[]} + * corresponding to the text. The format is simple: Each line may + * start with a hex offset followed by a colon (which is verified + * and presumably used just as a comment), and then consists of + * hex digits freely interspersed with whitespace. If a pound sign + * is encountered, it and the rest of the line are ignored as a + * comment. If a double quote is encountered, then the ASCII value + * of the subsequent characters is used, until the next double + * quote. Quoted strings may not span multiple lines. + * + * @param src {@code non-null;} the source string + * @return {@code non-null;} the parsed form + */ + public static byte[] parse(String src) { + int len = src.length(); + byte[] result = new byte[len / 2]; + int at = 0; + int outAt = 0; + + while (at < len) { + int nlAt = src.indexOf('\n', at); + if (nlAt < 0) { + nlAt = len; + } + int poundAt = src.indexOf('#', at); + + String line; + if ((poundAt >= 0) && (poundAt < nlAt)) { + line = src.substring(at, poundAt); + } else { + line = src.substring(at, nlAt); + } + at = nlAt + 1; + + int colonAt = line.indexOf(':'); + + atCheck: if (colonAt != -1) { + int quoteAt = line.indexOf('\"'); + if ((quoteAt != -1) && (quoteAt < colonAt)) { + break atCheck; + } - /** - * Parses the given text as hex, returning a {@code byte[]} - * corresponding to the text. The format is simple: Each line may - * start with a hex offset followed by a colon (which is verified - * and presumably used just as a comment), and then consists of - * hex digits freely interspersed with whitespace. If a pound sign - * is encountered, it and the rest of the line are ignored as a - * comment. If a double quote is encountered, then the ASCII value - * of the subsequent characters is used, until the next double - * quote. Quoted strings may not span multiple lines. - * - * @param src {@code non-null;} the source string - * @return {@code non-null;} the parsed form - */ - public static byte[] parse(String src) { - int len = src.length(); - byte[] result = new byte[len / 2]; - int at = 0; - int outAt = 0; - - while (at < len) { - int nlAt = src.indexOf('\n', at); - if (nlAt < 0) { - nlAt = len; - } - int poundAt = src.indexOf('#', at); - - String line; - if ((poundAt >= 0) && (poundAt < nlAt)) { - line = src.substring(at, poundAt); - } else { - line = src.substring(at, nlAt); - } - at = nlAt + 1; - - int colonAt = line.indexOf(':'); - - atCheck: - if (colonAt != -1) { - int quoteAt = line.indexOf('\"'); - if ((quoteAt != -1) && (quoteAt < colonAt)) { - break atCheck; - } - - String atStr = line.substring(0, colonAt).trim(); - line = line.substring(colonAt + 1); - int alleged = Integer.parseInt(atStr, 16); - if (alleged != outAt) { - throw new RuntimeException("bogus offset marker: " + - atStr); - } - } - - int lineLen = line.length(); - int value = -1; - boolean quoteMode = false; - - for (int i = 0; i < lineLen; i++) { - char c = line.charAt(i); - - if (quoteMode) { - if (c == '\"') { - quoteMode = false; - } else { - result[outAt] = (byte) c; - outAt++; - } - continue; - } - - if (c <= ' ') { - continue; - } - if (c == '\"') { - if (value != -1) { - throw new RuntimeException("spare digit around " + - "offset " + Hex.u4(outAt)); - } - quoteMode = true; - continue; - } - - int digVal = Character.digit(c, 16); - if (digVal == -1) { - throw new RuntimeException("bogus digit character: \"" + - c + "\""); - } - if (value == -1) { - value = digVal; - } else { - result[outAt] = (byte) ((value << 4) | digVal); - outAt++; - value = -1; - } - } - - if (value != -1) { - throw new RuntimeException("spare digit around offset " + - Hex.u4(outAt)); - } - - if (quoteMode) { - throw new RuntimeException("unterminated quote around " + - "offset " + Hex.u4(outAt)); - } + String atStr = line.substring(0, colonAt).trim(); + line = line.substring(colonAt + 1); + int alleged = Integer.parseInt(atStr, 16); + if (alleged != outAt) { + throw new RuntimeException("bogus offset marker: " + atStr); + } + } + + int lineLen = line.length(); + int value = -1; + boolean quoteMode = false; + + for (int i = 0; i < lineLen; i++) { + char c = line.charAt(i); + + if (quoteMode) { + if (c == '\"') { + quoteMode = false; + } else { + result[outAt] = (byte) c; + outAt++; + } + continue; } - if (outAt < result.length) { - byte[] newr = new byte[outAt]; - System.arraycopy(result, 0, newr, 0, outAt); - result = newr; + if (c <= ' ') { + continue; + } + if (c == '\"') { + if (value != -1) { + throw new RuntimeException("spare digit around " + "offset " + Hex.u4(outAt)); + } + quoteMode = true; + continue; } - return result; + int digVal = Character.digit(c, 16); + if (digVal == -1) { + throw new RuntimeException("bogus digit character: \"" + c + "\""); + } + if (value == -1) { + value = digVal; + } else { + result[outAt] = (byte) ((value << 4) | digVal); + outAt++; + value = -1; + } + } + + if (value != -1) { + throw new RuntimeException("spare digit around offset " + Hex.u4(outAt)); + } + + if (quoteMode) { + throw new RuntimeException("unterminated quote around " + "offset " + Hex.u4(outAt)); + } } + + if (outAt < result.length) { + byte[] newr = new byte[outAt]; + System.arraycopy(result, 0, newr, 0, outAt); + result = newr; + } + + return result; + } } diff --git a/dx/src/com/android/jack/dx/util/IndentingWriter.java b/dx/src/com/android/jack/dx/util/IndentingWriter.java index 5fa042a..b7a8e41 100644 --- a/dx/src/com/android/jack/dx/util/IndentingWriter.java +++ b/dx/src/com/android/jack/dx/util/IndentingWriter.java @@ -27,143 +27,143 @@ import java.io.Writer; * line. */ public final class IndentingWriter extends FilterWriter { - /** {@code null-ok;} optional prefix for every line */ - private final String prefix; + /** {@code null-ok;} optional prefix for every line */ + private final String prefix; - /** {@code > 0;} the maximum output width */ - private final int width; + /** {@code > 0;} the maximum output width */ + private final int width; - /** {@code > 0;} the maximum indent */ - private final int maxIndent; + /** {@code > 0;} the maximum indent */ + private final int maxIndent; - /** {@code >= 0;} current output column (zero-based) */ - private int column; + /** {@code >= 0;} current output column (zero-based) */ + private int column; - /** whether indent spaces are currently being collected */ - private boolean collectingIndent; + /** whether indent spaces are currently being collected */ + private boolean collectingIndent; - /** {@code >= 0;} current indent amount */ - private int indent; + /** {@code >= 0;} current indent amount */ + private int indent; - /** - * Constructs an instance. - * - * @param out {@code non-null;} writer to send final output to - * @param width {@code >= 0;} the maximum output width (not including - * {@code prefix}), or {@code 0} for no maximum - * @param prefix {@code non-null;} the prefix for each line - */ - public IndentingWriter(Writer out, int width, String prefix) { - super(out); + /** + * Constructs an instance. + * + * @param out {@code non-null;} writer to send final output to + * @param width {@code >= 0;} the maximum output width (not including + * {@code prefix}), or {@code 0} for no maximum + * @param prefix {@code 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; + if (out == null) { + throw new NullPointerException("out == null"); + } - bol(); + if (width < 0) { + throw new IllegalArgumentException("width < 0"); } - /** - * Constructs a no-prefix instance. - * - * @param out {@code non-null;} writer to send final output to - * @param width {@code >= 0;} the maximum output width (not including - * {@code prefix}), or {@code 0} for no maximum - */ - public IndentingWriter(Writer out, int width) { - this(out, width, ""); + if (prefix == null) { + throw new NullPointerException("prefix == null"); } - /** {@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++; - } + 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 {@code non-null;} writer to send final output to + * @param width {@code >= 0;} the maximum output width (not including + * {@code prefix}), or {@code 0} 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; } - } + } - /** {@inheritDoc} */ - @Override - public void write(char[] cbuf, int off, int len) throws IOException { - synchronized (lock) { - while (len > 0) { - write(cbuf[off]); - off++; - len--; - } + 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); } - } - /** {@inheritDoc} */ - @Override - public void write(String str, int off, int len) throws IOException { - synchronized (lock) { - while (len > 0) { - write(str.charAt(off)); - off++; - len--; - } + if (!collectingIndent) { + for (int i = 0; i < indent; i++) { + out.write(' '); + } + column = indent; } - } + } - /** - * Indicates that output is at the beginning of a line. - */ - private void bol() { - column = 0; - collectingIndent = (maxIndent != 0); - indent = 0; + out.write(c); + + if (c == '\n') { + bol(); + } else { + column++; + } + } + } + + /** {@inheritDoc} */ + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + synchronized (lock) { + while (len > 0) { + write(cbuf[off]); + off++; + len--; + } + } + } + + /** {@inheritDoc} */ + @Override + public void write(String str, int off, int len) throws IOException { + synchronized (lock) { + while (len > 0) { + write(str.charAt(off)); + off++; + len--; + } } + } + + /** + * Indicates that output is at the beginning of a line. + */ + private void bol() { + column = 0; + collectingIndent = (maxIndent != 0); + indent = 0; + } } diff --git a/dx/src/com/android/jack/dx/util/IntIterator.java b/dx/src/com/android/jack/dx/util/IntIterator.java index cd28378..8fca6fc 100644 --- a/dx/src/com/android/jack/dx/util/IntIterator.java +++ b/dx/src/com/android/jack/dx/util/IntIterator.java @@ -21,18 +21,18 @@ package com.android.jack.dx.util; */ public interface IntIterator { - /** - * Checks to see if the iterator has a next value. - * - * @return true if next() will succeed - */ - boolean hasNext(); + /** + * Checks to see if the iterator has a next value. + * + * @return true if next() will succeed + */ + boolean hasNext(); - /** - * Returns the next value in the iterator. - * - * @return next value - * @throws java.util.NoSuchElementException if no next element exists - */ - int next(); + /** + * Returns the next value in the iterator. + * + * @return next value + * @throws java.util.NoSuchElementException if no next element exists + */ + int next(); } diff --git a/dx/src/com/android/jack/dx/util/IntList.java b/dx/src/com/android/jack/dx/util/IntList.java index 0a7ce7c..36e96d0 100644 --- a/dx/src/com/android/jack/dx/util/IntList.java +++ b/dx/src/com/android/jack/dx/util/IntList.java @@ -22,432 +22,431 @@ import java.util.Arrays; * Simple list of {@code int}s. */ public final class IntList extends MutabilityControl { - /** {@code non-null;} immutable, no-element instance */ - public static final IntList EMPTY = new IntList(0); - - /** {@code non-null;} array of elements */ - private int[] values; - - /** {@code >= 0;} current size of the list */ - private int size; - - /** whether the values are currently sorted */ - private boolean sorted; - - static { - EMPTY.setImmutable(); + /** {@code non-null;} immutable, no-element instance */ + public static final IntList EMPTY = new IntList(0); + + /** {@code non-null;} array of elements */ + private int[] values; + + /** {@code >= 0;} current size of the list */ + private int size; + + /** whether the values are currently sorted */ + private boolean sorted; + + static { + EMPTY.setImmutable(); + } + + /** + * Constructs a new immutable instance with the given element. + * + * @param value the sole value in the list + */ + public static IntList makeImmutable(int value) { + IntList result = new IntList(1); + + result.add(value); + result.setImmutable(); + + return result; + } + + /** + * Constructs a new immutable instance with the given elements. + * + * @param value0 the first value in the list + * @param value1 the second value in the list + */ + public static IntList makeImmutable(int value0, int value1) { + IntList result = new IntList(2); + + result.add(value0); + result.add(value1); + result.setImmutable(); + + return result; + } + + /** + * Constructs an empty instance with a default initial capacity. + */ + public IntList() { + this(4); + } + + /** + * Constructs an empty instance. + * + * @param initialCapacity {@code >= 0;} initial capacity of the list + */ + public IntList(int initialCapacity) { + super(true); + + try { + values = new int[initialCapacity]; + } catch (NegativeArraySizeException ex) { + // Translate the exception. + throw new IllegalArgumentException("size < 0"); } - /** - * Constructs a new immutable instance with the given element. - * - * @param value the sole value in the list - */ - public static IntList makeImmutable(int value) { - IntList result = new IntList(1); + size = 0; + sorted = true; + } - result.add(value); - result.setImmutable(); + /** {@inheritDoc} */ + @Override + public int hashCode() { + int result = 0; - return result; + for (int i = 0; i < size; i++) { + result = (result * 31) + values[i]; } - /** - * Constructs a new immutable instance with the given elements. - * - * @param value0 the first value in the list - * @param value1 the second value in the list - */ - public static IntList makeImmutable(int value0, int value1) { - IntList result = new IntList(2); - - result.add(value0); - result.add(value1); - result.setImmutable(); + return result; + } - return result; + /** {@inheritDoc} */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; } - /** - * Constructs an empty instance with a default initial capacity. - */ - public IntList() { - this(4); + if (!(other instanceof IntList)) { + return false; } - /** - * Constructs an empty instance. - * - * @param initialCapacity {@code >= 0;} initial capacity of the list - */ - public IntList(int initialCapacity) { - super(true); - - try { - values = new int[initialCapacity]; - } catch (NegativeArraySizeException ex) { - // Translate the exception. - throw new IllegalArgumentException("size < 0"); - } + IntList otherList = (IntList) other; - size = 0; - sorted = true; + if (sorted != otherList.sorted) { + return false; } - /** {@inheritDoc} */ - @Override - public int hashCode() { - int result = 0; - - for (int i = 0; i < size; i++) { - result = (result * 31) + values[i]; - } - - return result; + if (size != otherList.size) { + return false; } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (! (other instanceof IntList)) { - return false; - } - - IntList otherList = (IntList) other; - - if (sorted != otherList.sorted) { - return false; - } - - if (size != otherList.size) { - return false; - } - - for (int i = 0; i < size; i++) { - if (values[i] != otherList.values[i]) { - return false; - } - } - - return true; + for (int i = 0; i < size; i++) { + if (values[i] != otherList.values[i]) { + return false; + } } - /** {@inheritDoc} */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(size * 5 + 10); - - sb.append('{'); + return true; + } - for (int i = 0; i < size; i++) { - if (i != 0) { - sb.append(", "); - } - sb.append(values[i]); - } + /** {@inheritDoc} */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(size * 5 + 10); - sb.append('}'); + sb.append('{'); - return sb.toString(); + for (int i = 0; i < size; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(values[i]); } - /** - * Gets the number of elements in this list. - */ - public int size() { - return size; + sb.append('}'); + + return sb.toString(); + } + + /** + * Gets the number of elements in this list. + */ + public int size() { + return size; + } + + /** + * Gets the indicated value. + * + * @param n {@code >= 0, < size();} which element + * @return the indicated element's value + */ + public int get(int n) { + if (n >= size) { + throw new IndexOutOfBoundsException("n >= size()"); } - /** - * Gets the indicated value. - * - * @param n {@code >= 0, < size();} which element - * @return the indicated element's value - */ - public int get(int n) { - if (n >= size) { - throw new IndexOutOfBoundsException("n >= size()"); - } - - try { - return values[n]; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate exception. - throw new IndexOutOfBoundsException("n < 0"); - } + try { + return values[n]; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate exception. + throw new IndexOutOfBoundsException("n < 0"); } - - /** - * Sets the value at the given index. - * - * @param n {@code >= 0, < size();} which element - * @param value value to store - */ - public void set(int n, int value) { - throwIfImmutable(); - - if (n >= size) { - throw new IndexOutOfBoundsException("n >= size()"); - } - - try { - values[n] = value; - sorted = false; - } catch (ArrayIndexOutOfBoundsException ex) { - // Translate the exception. - if (n < 0) { - throw new IllegalArgumentException("n < 0"); - } - } + } + + /** + * Sets the value at the given index. + * + * @param n {@code >= 0, < size();} which element + * @param value value to store + */ + public void set(int n, int value) { + throwIfImmutable(); + + if (n >= size) { + throw new IndexOutOfBoundsException("n >= size()"); } - /** - * Adds an element to the end of the list. This will increase the - * list's capacity if necessary. - * - * @param value the value to add - */ - public void add(int value) { - throwIfImmutable(); - - growIfNeeded(); - - values[size++] = value; - - if (sorted && (size > 1)) { - sorted = (value >= values[size - 2]); - } + try { + values[n] = value; + sorted = false; + } catch (ArrayIndexOutOfBoundsException ex) { + // Translate the exception. + if (n < 0) { + throw new IllegalArgumentException("n < 0"); + } } + } - /** - * Inserts element into specified index, moving elements at and above - * that index up one. May not be used to insert at an index beyond the - * current size (that is, insertion as a last element is legal but - * no further). - * - * @param n {@code >= 0, <=size();} index of where to insert - * @param value value to insert - */ - public void insert(int n, int value) { - if (n > size) { - throw new IndexOutOfBoundsException("n > size()"); - } + /** + * Adds an element to the end of the list. This will increase the + * list's capacity if necessary. + * + * @param value the value to add + */ + public void add(int value) { + throwIfImmutable(); - growIfNeeded(); + growIfNeeded(); - System.arraycopy (values, n, values, n+1, size - n); - values[n] = value; - size++; + values[size++] = value; - sorted = sorted - && (n == 0 || value > values[n-1]) - && (n == (size - 1) || value < values[n+1]); + if (sorted && (size > 1)) { + sorted = (value >= values[size - 2]); } - - /** - * Removes an element at a given index, shifting elements at greater - * indicies down one. - * - * @param n {@code >=0, < size();} index of element to remove - */ - public void removeIndex(int n) { - if (n >= size) { - throw new IndexOutOfBoundsException("n >= size()"); - } - - System.arraycopy (values, n + 1, values, n, size - n - 1); - size--; - - // sort status is unchanged + } + + /** + * Inserts element into specified index, moving elements at and above + * that index up one. May not be used to insert at an index beyond the + * current size (that is, insertion as a last element is legal but + * no further). + * + * @param n {@code >= 0, <=size();} index of where to insert + * @param value value to insert + */ + public void insert(int n, int value) { + if (n > size) { + throw new IndexOutOfBoundsException("n > size()"); } - /** - * Increases size of array if needed - */ - private void growIfNeeded() { - if (size == values.length) { - // Resize. - int[] newv = new int[size * 3 / 2 + 10]; - System.arraycopy(values, 0, newv, 0, size); - values = newv; - } + growIfNeeded(); + + System.arraycopy(values, n, values, n + 1, size - n); + values[n] = value; + size++; + + sorted = + sorted && (n == 0 || value > values[n - 1]) && (n == (size - 1) || value < values[n + 1]); + } + + /** + * Removes an element at a given index, shifting elements at greater + * indicies down one. + * + * @param n {@code >=0, < size();} index of element to remove + */ + public void removeIndex(int n) { + if (n >= size) { + throw new IndexOutOfBoundsException("n >= size()"); } - /** - * Returns the last element in the array without modifying the array - * - * @return last value in the array - * @throws IndexOutOfBoundsException if stack is empty - */ - public int top() { - return get(size - 1); + System.arraycopy(values, n + 1, values, n, size - n - 1); + size--; + + // sort status is unchanged + } + + /** + * Increases size of array if needed + */ + private void growIfNeeded() { + if (size == values.length) { + // Resize. + int[] newv = new int[size * 3 / 2 + 10]; + System.arraycopy(values, 0, newv, 0, size); + values = newv; } - - /** - * Pops an element off the end of the list and decreasing the size by one. - * - * @return value from what was the last element - * @throws IndexOutOfBoundsException if stack is empty - */ - public int pop() { - throwIfImmutable(); - - int result; - - result = get(size-1); - size--; - - return result; + } + + /** + * Returns the last element in the array without modifying the array + * + * @return last value in the array + * @throws IndexOutOfBoundsException if stack is empty + */ + public int top() { + return get(size - 1); + } + + /** + * Pops an element off the end of the list and decreasing the size by one. + * + * @return value from what was the last element + * @throws IndexOutOfBoundsException if stack is empty + */ + public int pop() { + throwIfImmutable(); + + int result; + + result = get(size - 1); + size--; + + return result; + } + + /** + * Pops N elements off the end of the list and decreasing the size by N. + * + * @param n {@code >= 0;} number of elements to remove from end + * @throws IndexOutOfBoundsException if stack is smaller than N + */ + public void pop(int n) { + throwIfImmutable(); + + size -= n; + } + + /** + * Shrinks the size of the list. + * + * @param newSize {@code >= 0;} the new size + */ + public void shrink(int newSize) { + if (newSize < 0) { + throw new IllegalArgumentException("newSize < 0"); } - /** - * Pops N elements off the end of the list and decreasing the size by N. - * - * @param n {@code >= 0;} number of elements to remove from end - * @throws IndexOutOfBoundsException if stack is smaller than N - */ - public void pop(int n) { - throwIfImmutable(); - - size -= n; + if (newSize > size) { + throw new IllegalArgumentException("newSize > size"); } - /** - * Shrinks the size of the list. - * - * @param newSize {@code >= 0;} the new size - */ - public void shrink(int newSize) { - if (newSize < 0) { - throw new IllegalArgumentException("newSize < 0"); - } + throwIfImmutable(); - if (newSize > size) { - throw new IllegalArgumentException("newSize > size"); - } + size = newSize; + } - throwIfImmutable(); + /** + * Makes and returns a mutable copy of the list. + * + * @return {@code non-null;} an appropriately-constructed instance + */ + public IntList mutableCopy() { + int sz = size; + IntList result = new IntList(sz); - size = newSize; + for (int i = 0; i < sz; i++) { + result.add(values[i]); } - /** - * Makes and returns a mutable copy of the list. - * - * @return {@code non-null;} an appropriately-constructed instance - */ - public IntList mutableCopy() { - int sz = size; - IntList result = new IntList(sz); + return result; + } - for (int i = 0; i < sz; i++) { - result.add(values[i]); - } + /** + * Sorts the elements in the list in-place. + */ + public void sort() { + throwIfImmutable(); - return result; + if (!sorted) { + Arrays.sort(values, 0, size); + sorted = true; } - - /** - * Sorts the elements in the list in-place. - */ - public void sort() { - throwIfImmutable(); - - if (!sorted) { - Arrays.sort(values, 0, size); - sorted = true; + } + + /** + * Returns the index of the given value, or -1 if the value does not + * appear in the list. This will do a binary search if the list is + * sorted or a linear search if not. + * + * @param value value to find + * @return index of value or -1 + */ + public int indexOf(int value) { + int ret = binarysearch(value); + + return ret >= 0 ? ret : -1; + + } + + /** + * Performs a binary search on a sorted list, returning the index of + * the given value if it is present or + * {@code (-(insertion point) - 1)} if the value is not present. + * If the list is not sorted, then reverts to linear search and returns + * {@code -size()} if the element is not found. + * + * @param value value to find + * @return index of value or {@code (-(insertion point) - 1)} if the + * value is not present + */ + public int binarysearch(int value) { + int sz = size; + + if (!sorted) { + // Linear search. + for (int i = 0; i < sz; i++) { + if (values[i] == value) { + return i; } - } - - /** - * Returns the index of the given value, or -1 if the value does not - * appear in the list. This will do a binary search if the list is - * sorted or a linear search if not. - * - * @param value value to find - * @return index of value or -1 - */ - public int indexOf(int value) { - int ret = binarysearch(value); - - return ret >= 0 ? ret : -1; + } + return -sz; } - /** - * Performs a binary search on a sorted list, returning the index of - * the given value if it is present or - * {@code (-(insertion point) - 1)} if the value is not present. - * If the list is not sorted, then reverts to linear search and returns - * {@code -size()} if the element is not found. - * - * @param value value to find - * @return index of value or {@code (-(insertion point) - 1)} if the - * value is not present + /* + * Binary search. This variant does only one value comparison + * per iteration but does one more iteration on average than + * the variant that includes a value equality check per + * iteration. */ - public int binarysearch(int value) { - int sz = size; - - if (!sorted) { - // Linear search. - for (int i = 0; i < sz; i++) { - if (values[i] == value) { - return i; - } - } - - return -sz; - } - /* - * Binary search. This variant does only one value comparison - * per iteration but does one more iteration on average than - * the variant that includes a value equality check per - * iteration. - */ - - int min = -1; - int max = sz; - - while (max > (min + 1)) { - /* - * The guessIdx calculation is equivalent to ((min + max) - * / 2) but won't go wonky when min and max are close to - * Integer.MAX_VALUE. - */ - int guessIdx = min + ((max - min) >> 1); - int guess = values[guessIdx]; - - if (value <= guess) { - max = guessIdx; - } else { - min = guessIdx; - } - } - - if ((max != sz)) { - return (value == values[max]) ? max : (-max - 1); - } else { - return -sz - 1; - } +int min = -1; + int max = sz; + + while (max > (min + 1)) { + /* + * The guessIdx calculation is equivalent to ((min + max) + * / 2) but won't go wonky when min and max are close to + * Integer.MAX_VALUE. + */ + int guessIdx = min + ((max - min) >> 1); + int guess = values[guessIdx]; + + if (value <= guess) { + max = guessIdx; + } else { + min = guessIdx; + } } - - /** - * Returns whether or not the given value appears in the list. - * This will do a binary search if the list is sorted or a linear - * search if not. - * - * @see #sort - * - * @param value value to look for - * @return whether the list contains the given value - */ - public boolean contains(int value) { - return indexOf(value) >= 0; + if ((max != sz)) { + return (value == values[max]) ? max : (-max - 1); + } else { + return -sz - 1; } + } + + + /** + * Returns whether or not the given value appears in the list. + * This will do a binary search if the list is sorted or a linear + * search if not. + * + * @see #sort + * + * @param value value to look for + * @return whether the list contains the given value + */ + public boolean contains(int value) { + return indexOf(value) >= 0; + } } diff --git a/dx/src/com/android/jack/dx/util/IntSet.java b/dx/src/com/android/jack/dx/util/IntSet.java index 9d056fe..71fc7d3 100644 --- a/dx/src/com/android/jack/dx/util/IntSet.java +++ b/dx/src/com/android/jack/dx/util/IntSet.java @@ -21,47 +21,47 @@ package com.android.jack.dx.util; */ public interface IntSet { - /** - * Adds an int to a set - * - * @param value int to add - */ - void add(int value); + /** + * Adds an int to a set + * + * @param value int to add + */ + void add(int value); - /** - * Removes an int from a set. - * - * @param value int to remove - */ - void remove(int value); + /** + * Removes an int from a set. + * + * @param value int to remove + */ + void remove(int value); - /** - * Checks to see if a value is in the set - * - * @param value int to check - * @return true if in set - */ - boolean has(int value); + /** + * Checks to see if a value is in the set + * + * @param value int to check + * @return true if in set + */ + boolean has(int value); - /** - * Merges {@code other} into this set, so this set becomes the - * union of the two. - * - * @param other {@code non-null;} other set to merge with. - */ - void merge(IntSet other); + /** + * Merges {@code other} into this set, so this set becomes the + * union of the two. + * + * @param other {@code non-null;} other set to merge with. + */ + void merge(IntSet other); - /** - * Returns the count of unique elements in this set. - * - * @return {@code > = 0;} count of unique elements - */ - int elements(); + /** + * Returns the count of unique elements in this set. + * + * @return {@code > = 0;} count of unique elements + */ + int elements(); - /** - * Iterates the set - * - * @return {@code non-null;} a set iterator - */ - IntIterator iterator(); + /** + * Iterates the set + * + * @return {@code non-null;} a set iterator + */ + IntIterator iterator(); } diff --git a/dx/src/com/android/jack/dx/util/LabeledItem.java b/dx/src/com/android/jack/dx/util/LabeledItem.java index 3883e38..d609c40 100644 --- a/dx/src/com/android/jack/dx/util/LabeledItem.java +++ b/dx/src/com/android/jack/dx/util/LabeledItem.java @@ -21,10 +21,10 @@ package com.android.jack.dx.util; */ public interface LabeledItem { - /* - * Gets the label of this block. - * - * @return {@code >= 0;} the label - */ - public int getLabel(); + /* + * Gets the label of this block. + * + * @return {@code >= 0;} the label + */ + public int getLabel(); } diff --git a/dx/src/com/android/jack/dx/util/LabeledList.java b/dx/src/com/android/jack/dx/util/LabeledList.java index 323d997..3432e2c 100644 --- a/dx/src/com/android/jack/dx/util/LabeledList.java +++ b/dx/src/com/android/jack/dx/util/LabeledList.java @@ -22,166 +22,167 @@ import java.util.Arrays; * A list of labeled items, allowing easy lookup by label. */ public class LabeledList extends FixedSizeList { - /** - * Sparse array indexed by label to FixedSizeList index; - * {@code -1} for an invalid label. - */ - private final IntList labelToIndex; - - /** @inheritDoc */ - public LabeledList(int size) { - super(size); - - labelToIndex = new IntList(size); + /** + * Sparse array indexed by label to FixedSizeList index; + * {@code -1} for an invalid label. + */ + private final IntList labelToIndex; + + /** @inheritDoc */ + public LabeledList(int size) { + super(size); + + labelToIndex = new IntList(size); + } + + /** + * Constructs a new instance that is a copy of the old instance. + * + * @param old instance to copy + */ + public LabeledList(LabeledList old) { + super(old.size()); + labelToIndex = old.labelToIndex.mutableCopy(); + + int sz = old.size(); + + for (int i = 0; i < sz; i++) { + Object one = old.get0(i); + if (one != null) { + set0(i, one); + } } - - /** - * Constructs a new instance that is a copy of the old instance. - * - * @param old instance to copy - */ - public LabeledList(LabeledList old) { - super(old.size()); - labelToIndex = old.labelToIndex.mutableCopy(); - - int sz = old.size(); - - for (int i = 0; i < sz; i++) { - Object one = old.get0(i); - if (one != null) { - set0(i, one); - } - } + } + + /** + * Gets the maximum label (exclusive) of any block added to this instance. + * + * @return {@code >= 0;} the maximum label + */ + public final int getMaxLabel() { + int sz = labelToIndex.size(); + + // Gobble any deleted labels that may be at the end. + int i; + for (i = sz - 1; (i >= 0) && (labelToIndex.get(i) < 0); i--) { + /* empty */ } - /** - * Gets the maximum label (exclusive) of any block added to this instance. - * - * @return {@code >= 0;} the maximum label - */ - public final int getMaxLabel() { - int sz = labelToIndex.size(); - - // Gobble any deleted labels that may be at the end. - int i; - for (i = sz - 1; (i >= 0) && (labelToIndex.get(i) < 0); i--) - /*empty*/ ; - - int newSize = i + 1; - - labelToIndex.shrink(newSize); - - return newSize; + int newSize = i + 1; + + labelToIndex.shrink(newSize); + + return newSize; + } + + /** + * Removes a label from the label-to-index mapping. + * + * @param oldLabel label to remove + */ + private void removeLabel(int oldLabel) { + labelToIndex.set(oldLabel, -1); + } + + /** + * Adds a label and index to the label-to-index mapping. + * + * @param label new label + * @param index index of block. + */ + private void addLabelIndex(int label, int index) { + int origSz = labelToIndex.size(); + + for (int i = 0; i <= (label - origSz); i++) { + labelToIndex.add(-1); } - /** - * Removes a label from the label-to-index mapping. - * - * @param oldLabel label to remove - */ - private void removeLabel(int oldLabel) { - labelToIndex.set(oldLabel, -1); + labelToIndex.set(label, index); + } + + /** + * Gets the index of the first item in the list with the given + * label, if any. + * + * @param label {@code >= 0;} the label to look for + * @return {@code >= -1;} the index of the so-labelled item, or {@code -1} + * if none is found + */ + public final int indexOfLabel(int label) { + if (label >= labelToIndex.size()) { + return -1; + } else { + return labelToIndex.get(label); } - - /** - * Adds a label and index to the label-to-index mapping. - * - * @param label new label - * @param index index of block. - */ - private void addLabelIndex(int label, int index) { - int origSz = labelToIndex.size(); - - for (int i = 0; i <= (label - origSz); i++) { - labelToIndex.add(-1); - } - - labelToIndex.set(label, index); + } + + /** + * Gets an array containing all of the labels used in this instance, + * in order. The returned array is freshly-allocated and so may be + * modified safely by the caller without impacting this instance. + * + * @return {@code non-null;} ordered array of labels + * @throws NullPointerException thrown if there are any {@code null} + * items in this instance + */ + public final int[] getLabelsInOrder() { + int sz = size(); + int[] result = new int[sz]; + + for (int i = 0; i < sz; i++) { + LabeledItem li = (LabeledItem) get0(i); + if (li == null) { + throw new NullPointerException("null at index " + i); + } + result[i] = li.getLabel(); } - /** - * Gets the index of the first item in the list with the given - * label, if any. - * - * @param label {@code >= 0;} the label to look for - * @return {@code >= -1;} the index of the so-labelled item, or {@code -1} - * if none is found - */ - public final int indexOfLabel(int label) { - if (label >= labelToIndex.size()) { - return -1; - } else { - return labelToIndex.get(label); - } - } + Arrays.sort(result); + return result; + } - /** - * Gets an array containing all of the labels used in this instance, - * in order. The returned array is freshly-allocated and so may be - * modified safely by the caller without impacting this instance. - * - * @return {@code non-null;} ordered array of labels - * @throws NullPointerException thrown if there are any {@code null} - * items in this instance - */ - public final int[] getLabelsInOrder() { - int sz = size(); - int[] result = new int[sz]; - - for (int i = 0; i < sz; i++) { - LabeledItem li = (LabeledItem) get0(i); - if (li == null) { - throw new NullPointerException("null at index " + i); - } - result[i] = li.getLabel(); - } - - Arrays.sort(result); - return result; - } + /** @inheritDoc */ + @Override + public void shrinkToFit() { + super.shrinkToFit(); - /** @inheritDoc */ - @Override - public void shrinkToFit() { - super.shrinkToFit(); + rebuildLabelToIndex(); + } - rebuildLabelToIndex(); - } + /** + * Rebuilds the label-to-index mapping after a {@code shrinkToFit()}. + * Note: This assumes that the labels that are in the list are the + * same, although the indicies may have changed. + */ + private void rebuildLabelToIndex() { + int szItems = size(); + + for (int i = 0; i < szItems; i++) { + LabeledItem li = (LabeledItem) get0(i); - /** - * Rebuilds the label-to-index mapping after a {@code shrinkToFit()}. - * Note: This assumes that the labels that are in the list are the - * same, although the indicies may have changed. - */ - private void rebuildLabelToIndex() { - int szItems = size(); - - for (int i = 0; i < szItems; i++) { - LabeledItem li = (LabeledItem) get0(i); - - if (li != null) { - labelToIndex.set(li.getLabel(), i); - } - } + if (li != null) { + labelToIndex.set(li.getLabel(), i); + } } + } - /** - * Sets the element at the given index. - * - * @param n {@code >= 0, < size();} which element - * @param item {@code null-ok;} the value to store - */ - protected void set(int n, LabeledItem item) { - LabeledItem old = (LabeledItem) getOrNull0(n); + /** + * Sets the element at the given index. + * + * @param n {@code >= 0, < size();} which element + * @param item {@code null-ok;} the value to store + */ + protected void set(int n, LabeledItem item) { + LabeledItem old = (LabeledItem) getOrNull0(n); - set0(n, item); + set0(n, item); - if (old != null) { - removeLabel(old.getLabel()); - } + if (old != null) { + removeLabel(old.getLabel()); + } - if (item != null) { - addLabelIndex(item.getLabel(), n); - } + if (item != null) { + addLabelIndex(item.getLabel(), n); } + } } diff --git a/dx/src/com/android/jack/dx/util/Leb128Utils.java b/dx/src/com/android/jack/dx/util/Leb128Utils.java index f6d85d1..9b5ee78 100644 --- a/dx/src/com/android/jack/dx/util/Leb128Utils.java +++ b/dx/src/com/android/jack/dx/util/Leb128Utils.java @@ -21,142 +21,140 @@ package com.android.jack.dx.util; * section 7.6. */ public final class Leb128Utils { - /** - * This class is uninstantiable. - */ - private Leb128Utils() { - // This space intentionally left blank. + /** + * 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(dx team): This could be much cleverer. + + int remaining = value >> 7; + int count = 0; + + while (remaining != 0) { + remaining >>= 7; + count++; } - /** - * 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) { - remaining >>= 7; - count++; - } - - return count + 1; + 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(dx team): 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++; } - /** - * 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; + return count; + } + + /** + * Reads an signed integer from {@code in}. + */ + public static int readSignedLeb128(ByteInput in) { + int result = 0; + int cur; + int count = 0; + int signBits = -1; + + do { + cur = in.readByte() & 0xff; + result |= (cur & 0x7f) << (count * 7); + signBits <<= 7; + count++; + } while (((cur & 0x80) == 0x80) && count < 5); + + if ((cur & 0x80) == 0x80) { + throw new DexException("invalid LEB128 sequence"); } - /** - * Reads an signed integer from {@code in}. - */ - public static int readSignedLeb128(ByteInput in) { - int result = 0; - int cur; - int count = 0; - int signBits = -1; - - do { - cur = in.readByte() & 0xff; - result |= (cur & 0x7f) << (count * 7); - signBits <<= 7; - count++; - } while (((cur & 0x80) == 0x80) && count < 5); - - if ((cur & 0x80) == 0x80) { - throw new DexException("invalid LEB128 sequence"); - } - - // Sign extend if appropriate - if (((signBits >> 1) & result) != 0 ) { - result |= signBits; - } - - return result; + // Sign extend if appropriate + if (((signBits >> 1) & result) != 0) { + result |= signBits; } - /** - * Reads an unsigned integer from {@code in}. - */ - public static int readUnsignedLeb128(ByteInput in) { - int result = 0; - int cur; - int count = 0; - - do { - cur = in.readByte() & 0xff; - result |= (cur & 0x7f) << (count * 7); - count++; - } while (((cur & 0x80) == 0x80) && count < 5); - - if ((cur & 0x80) == 0x80) { - throw new DexException("invalid LEB128 sequence"); - } - - return result; + return result; + } + + /** + * Reads an unsigned integer from {@code in}. + */ + public static int readUnsignedLeb128(ByteInput in) { + int result = 0; + int cur; + int count = 0; + + do { + cur = in.readByte() & 0xff; + result |= (cur & 0x7f) << (count * 7); + count++; + } while (((cur & 0x80) == 0x80) && count < 5); + + if ((cur & 0x80) == 0x80) { + throw new DexException("invalid LEB128 sequence"); } - /** - * Writes {@code value} as an unsigned integer to {@code out}, starting at - * {@code offset}. Returns the number of bytes written. - */ - public static void writeUnsignedLeb128(ByteOutput out, int value) { - int remaining = value >>> 7; + return result; + } - while (remaining != 0) { - out.writeByte((byte) ((value & 0x7f) | 0x80)); - value = remaining; - remaining >>>= 7; - } + /** + * Writes {@code value} as an unsigned integer to {@code out}, starting at + * {@code offset}. Returns the number of bytes written. + */ + public static void writeUnsignedLeb128(ByteOutput out, int value) { + int remaining = value >>> 7; - out.writeByte((byte) (value & 0x7f)); + while (remaining != 0) { + out.writeByte((byte) ((value & 0x7f) | 0x80)); + value = remaining; + remaining >>>= 7; } - /** - * Writes {@code value} as a signed integer to {@code out}, starting at - * {@code offset}. Returns the number of bytes written. - */ - public static void writeSignedLeb128(ByteOutput out, int value) { - int remaining = value >> 7; - boolean hasMore = true; - int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; - - while (hasMore) { - hasMore = (remaining != end) - || ((remaining & 1) != ((value >> 6) & 1)); - - out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0))); - value = remaining; - remaining >>= 7; - } + out.writeByte((byte) (value & 0x7f)); + } + + /** + * Writes {@code value} as a signed integer to {@code out}, starting at + * {@code offset}. Returns the number of bytes written. + */ + public static void writeSignedLeb128(ByteOutput out, int value) { + int remaining = value >> 7; + boolean hasMore = true; + int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; + + while (hasMore) { + hasMore = (remaining != end) || ((remaining & 1) != ((value >> 6) & 1)); + + out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0))); + value = remaining; + remaining >>= 7; } + } } diff --git a/dx/src/com/android/jack/dx/util/ListIntSet.java b/dx/src/com/android/jack/dx/util/ListIntSet.java index 36f1891..bae44e8 100644 --- a/dx/src/com/android/jack/dx/util/ListIntSet.java +++ b/dx/src/com/android/jack/dx/util/ListIntSet.java @@ -23,110 +23,119 @@ import java.util.NoSuchElementException; */ public class ListIntSet implements IntSet { - /** also accessed in BitIntSet */ - final IntList ints; - - /** - * Constructs an instance - */ - public ListIntSet() { - ints = new IntList(); - ints.sort(); + /** also accessed in BitIntSet */ + final IntList ints; + + /** + * Constructs an instance + */ + public ListIntSet() { + ints = new IntList(); + ints.sort(); + } + + /** @inheritDoc */ + @Override + public void add(int value) { + int index = ints.binarysearch(value); + + if (index < 0) { + ints.insert(-(index + 1), value); } + } - /** @inheritDoc */ - public void add(int value) { - int index = ints.binarysearch(value); + /** @inheritDoc */ + @Override + public void remove(int value) { + int index = ints.indexOf(value); - if (index < 0) { - ints.insert(-(index + 1), value); - } + if (index >= 0) { + ints.removeIndex(index); } - - /** @inheritDoc */ - public void remove(int value) { - int index = ints.indexOf(value); - - if (index >= 0) { - ints.removeIndex(index); + } + + /** @inheritDoc */ + @Override + public boolean has(int value) { + return ints.indexOf(value) >= 0; + } + + /** @inheritDoc */ + @Override + public void merge(IntSet other) { + if (other instanceof ListIntSet) { + ListIntSet o = (ListIntSet) other; + int szThis = ints.size(); + int szOther = o.ints.size(); + + int i = 0; + int j = 0; + + while (j < szOther && i < szThis) { + while (j < szOther && o.ints.get(j) < ints.get(i)) { + add(o.ints.get(j++)); } - } - - /** @inheritDoc */ - public boolean has(int value) { - return ints.indexOf(value) >= 0; - } - - /** @inheritDoc */ - public void merge(IntSet other) { - if (other instanceof ListIntSet) { - ListIntSet o = (ListIntSet) other; - int szThis = ints.size(); - int szOther = o.ints.size(); - - int i = 0; - int j = 0; - - while (j < szOther && i < szThis) { - while (j < szOther && o.ints.get(j) < ints.get(i)) { - add(o.ints.get(j++)); - } - if (j == szOther) { - break; - } - while (i < szThis && o.ints.get(j) >= ints.get(i)) { - i++; - } - } - - while (j < szOther) { - add(o.ints.get(j++)); - } - - ints.sort(); - } else if (other instanceof BitIntSet) { - BitIntSet o = (BitIntSet) other; - - for (int i = 0; i >= 0; i = Bits.findFirst(o.bits, i + 1)) { - ints.add(i); - } - ints.sort(); - } else { - IntIterator iter = other.iterator(); - while (iter.hasNext()) { - add(iter.next()); - } + if (j == szOther) { + break; } + while (i < szThis && o.ints.get(j) >= ints.get(i)) { + i++; + } + } + + while (j < szOther) { + add(o.ints.get(j++)); + } + + ints.sort(); + } else if (other instanceof BitIntSet) { + BitIntSet o = (BitIntSet) other; + + for (int i = 0; i >= 0; i = Bits.findFirst(o.bits, i + 1)) { + ints.add(i); + } + ints.sort(); + } else { + IntIterator iter = other.iterator(); + while (iter.hasNext()) { + add(iter.next()); + } } + } + + /** @inheritDoc */ + @Override + public int elements() { + return ints.size(); + } + + /** @inheritDoc */ + @Override + public IntIterator iterator() { + return new IntIterator() { + private int idx = 0; + + /** @inheritDoc */ + @Override + public boolean hasNext() { + return idx < ints.size(); + } + + /** @inheritDoc */ + @Override + public int next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } - /** @inheritDoc */ - public int elements() { - return ints.size(); - } - - /** @inheritDoc */ - public IntIterator iterator() { - return new IntIterator() { - private int idx = 0; - - /** @inheritDoc */ - public boolean hasNext() { - return idx < ints.size(); - } - - /** @inheritDoc */ - public int next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - - return ints.get(idx++); - } - }; - } + return ints.get(idx++); + } + }; + } - /** @inheritDoc */ - public String toString() { - return ints.toString(); - } + /** @inheritDoc */ + @Override + public String toString() { + return ints.toString(); + } } diff --git a/dx/src/com/android/jack/dx/util/MutabilityControl.java b/dx/src/com/android/jack/dx/util/MutabilityControl.java index d716ffe..9e56656 100644 --- a/dx/src/com/android/jack/dx/util/MutabilityControl.java +++ b/dx/src/com/android/jack/dx/util/MutabilityControl.java @@ -23,67 +23,67 @@ package com.android.jack.dx.util; * to the checker in all the right places. */ public class MutabilityControl { - /** whether this instance is mutable */ - private boolean mutable; + /** whether this instance is mutable */ + private boolean mutable; - /** - * Constructs an instance. It is initially mutable. - */ - public MutabilityControl() { - mutable = true; - } + /** + * Constructs an instance. It is initially mutable. + */ + public MutabilityControl() { + mutable = true; + } - /** - * Constructs an instance, explicitly indicating the mutability. - * - * @param mutable {@code true} iff this instance is mutable - */ - public MutabilityControl(boolean mutable) { - this.mutable = mutable; - } + /** + * Constructs an instance, explicitly indicating the mutability. + * + * @param mutable {@code true} iff this instance is mutable + */ + public MutabilityControl(boolean mutable) { + this.mutable = mutable; + } - /** - * Makes this instance immutable. - */ - public void setImmutable() { - mutable = false; - } + /** + * Makes this instance immutable. + */ + public void setImmutable() { + mutable = false; + } - /** - * Checks to see whether or not this instance is immutable. This is the - * same as calling {@code !isMutable()}. - * - * @return {@code true} iff this instance is immutable - */ - public final boolean isImmutable() { - return !mutable; - } + /** + * Checks to see whether or not this instance is immutable. This is the + * same as calling {@code !isMutable()}. + * + * @return {@code true} iff this instance is immutable + */ + public final boolean isImmutable() { + return !mutable; + } - /** - * Checks to see whether or not this instance is mutable. - * - * @return {@code true} iff this instance is mutable - */ - public final boolean isMutable() { - return mutable; - } + /** + * Checks to see whether or not this instance is mutable. + * + * @return {@code true} iff this instance is mutable + */ + public final boolean isMutable() { + return mutable; + } - /** - * Throws {@link MutabilityException} if this instance is - * immutable. - */ - public final void throwIfImmutable() { - if (!mutable) { - throw new MutabilityException("immutable instance"); - } + /** + * Throws {@link MutabilityException} if this instance is + * immutable. + */ + public final void throwIfImmutable() { + if (!mutable) { + throw new MutabilityException("immutable instance"); } + } - /** - * Throws {@link MutabilityException} if this instance is mutable. - */ - public final void throwIfMutable() { - if (mutable) { - throw new MutabilityException("mutable instance"); - } + /** + * Throws {@link MutabilityException} if this instance is mutable. + */ + public final void throwIfMutable() { + if (mutable) { + throw new MutabilityException("mutable instance"); } + } } diff --git a/dx/src/com/android/jack/dx/util/MutabilityException.java b/dx/src/com/android/jack/dx/util/MutabilityException.java index 7e59883..b13a7d7 100644 --- a/dx/src/com/android/jack/dx/util/MutabilityException.java +++ b/dx/src/com/android/jack/dx/util/MutabilityException.java @@ -19,17 +19,19 @@ package com.android.jack.dx.util; /** * Exception due to a mutability problem. */ -public class MutabilityException - extends ExceptionWithContext { - public MutabilityException(String message) { - super(message); - } +public class MutabilityException extends ExceptionWithContext { - public MutabilityException(Throwable cause) { - super(cause); - } + private static final long serialVersionUID = 1L; - public MutabilityException(String message, Throwable cause) { - super(message, cause); - } + public MutabilityException(String message) { + super(message); + } + + public MutabilityException(Throwable cause) { + super(cause); + } + + public MutabilityException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/dx/src/com/android/jack/dx/util/Mutf8.java b/dx/src/com/android/jack/dx/util/Mutf8.java index 5f01693..e279525 100644 --- a/dx/src/com/android/jack/dx/util/Mutf8.java +++ b/dx/src/com/android/jack/dx/util/Mutf8.java @@ -24,91 +24,91 @@ import java.io.UTFDataFormatException; * <p>Derived from libcore's MUTF-8 encoder at java.nio.charset.ModifiedUtf8. */ public final class Mutf8 { - private Mutf8() {} + private Mutf8() {} - /** - * Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is - * encountered. Returns a new string containing the decoded characters. - */ - public static String decode(ByteInput in, char[] out) throws UTFDataFormatException { - int s = 0; - while (true) { - char a = (char) (in.readByte() & 0xff); - if (a == 0) { - return new String(out, 0, s); - } - out[s] = a; - if (a < '\u0080') { - s++; - } else if ((a & 0xe0) == 0xc0) { - int b = in.readByte() & 0xff; - if ((b & 0xC0) != 0x80) { - throw new UTFDataFormatException("bad second byte"); - } - out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F)); - } else if ((a & 0xf0) == 0xe0) { - int b = in.readByte() & 0xff; - int c = in.readByte() & 0xff; - if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) { - throw new UTFDataFormatException("bad second or third byte"); - } - out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)); - } else { - throw new UTFDataFormatException("bad byte"); - } + /** + * Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is + * encountered. Returns a new string containing the decoded characters. + */ + public static String decode(ByteInput in, char[] out) throws UTFDataFormatException { + int s = 0; + while (true) { + char a = (char) (in.readByte() & 0xff); + if (a == 0) { + return new String(out, 0, s); + } + out[s] = a; + if (a < '\u0080') { + s++; + } else if ((a & 0xe0) == 0xc0) { + int b = in.readByte() & 0xff; + if ((b & 0xC0) != 0x80) { + throw new UTFDataFormatException("bad second byte"); } - } - - /** - * Returns the number of bytes the modified UTF8 representation of 's' would take. - */ - private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException { - long result = 0; - final int length = s.length(); - for (int i = 0; i < length; ++i) { - char ch = s.charAt(i); - if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. - ++result; - } else if (ch <= 2047) { - result += 2; - } else { - result += 3; - } - if (shortLength && result > 65535) { - throw new UTFDataFormatException("String more than 65535 UTF bytes long"); - } + out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F)); + } else if ((a & 0xf0) == 0xe0) { + int b = in.readByte() & 0xff; + int c = in.readByte() & 0xff; + if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) { + throw new UTFDataFormatException("bad second or third byte"); } - return result; + out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)); + } else { + throw new UTFDataFormatException("bad byte"); + } } + } - /** - * Encodes the modified UTF-8 bytes corresponding to {@code s} into {@code - * dst}, starting at {@code offset}. - */ - public static void encode(byte[] dst, int offset, String s) { - final int length = s.length(); - for (int i = 0; i < length; i++) { - char ch = s.charAt(i); - if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. - dst[offset++] = (byte) ch; - } else if (ch <= 2047) { - dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6))); - dst[offset++] = (byte) (0x80 | (0x3f & ch)); - } else { - dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12))); - dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6))); - dst[offset++] = (byte) (0x80 | (0x3f & ch)); - } - } + /** + * Returns the number of bytes the modified UTF8 representation of 's' would take. + */ + private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException { + long result = 0; + final int length = s.length(); + for (int i = 0; i < length; ++i) { + char ch = s.charAt(i); + if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. + ++result; + } else if (ch <= 2047) { + result += 2; + } else { + result += 3; + } + if (shortLength && result > 65535) { + throw new UTFDataFormatException("String more than 65535 UTF bytes long"); + } } + return result; + } - /** - * Returns an array containing the <i>modified UTF-8</i> form of {@code s}. - */ - public static byte[] encode(String s) throws UTFDataFormatException { - int utfCount = (int) countBytes(s, true); - byte[] result = new byte[utfCount]; - encode(result, 0, s); - return result; + /** + * Encodes the modified UTF-8 bytes corresponding to {@code s} into {@code + * dst}, starting at {@code offset}. + */ + public static void encode(byte[] dst, int offset, String s) { + final int length = s.length(); + for (int i = 0; i < length; i++) { + char ch = s.charAt(i); + if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. + dst[offset++] = (byte) ch; + } else if (ch <= 2047) { + dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6))); + dst[offset++] = (byte) (0x80 | (0x3f & ch)); + } else { + dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12))); + dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6))); + dst[offset++] = (byte) (0x80 | (0x3f & ch)); + } } + } + + /** + * Returns an array containing the <i>modified UTF-8</i> form of {@code s}. + */ + public static byte[] encode(String s) throws UTFDataFormatException { + int utfCount = (int) countBytes(s, true); + byte[] result = new byte[utfCount]; + encode(result, 0, s); + return result; + } } diff --git a/dx/src/com/android/jack/dx/util/Output.java b/dx/src/com/android/jack/dx/util/Output.java index 42640b7..d4a1a59 100644 --- a/dx/src/com/android/jack/dx/util/Output.java +++ b/dx/src/com/android/jack/dx/util/Output.java @@ -22,108 +22,109 @@ package com.android.jack.dx.util; * are declared, and multibyte output is defined to be little-endian. */ public interface Output extends ByteOutput { - /** - * Gets the current cursor position. This is the same as the number of - * bytes written to this instance. - * - * @return {@code >= 0;} the cursor position - */ - public int getCursor(); + /** + * Gets the current cursor position. This is the same as the number of + * bytes written to this instance. + * + * @return {@code >= 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} - */ - public void assertCursor(int expectedCursor); + /** + * Asserts that the cursor is the given value. + * + * @param expectedCursor the expected cursor value + * @throws RuntimeException thrown if {@code getCursor() != + * expectedCursor} + */ + public void assertCursor(int expectedCursor); - /** - * Writes a {@code byte} 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 byte} to this instance. + * + * @param value the value to write; all but the low 8 bits are ignored + */ + @Override + public void writeByte(int value); - /** - * Writes a {@code short} to this instance. - * - * @param value the value to write; all but the low 16 bits are ignored - */ - public void writeShort(int value); + /** + * Writes a {@code short} 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} to this instance. - * - * @param value the value to write - */ - public void writeInt(int value); + /** + * Writes an {@code int} to this instance. + * + * @param value the value to write + */ + public void writeInt(int value); - /** - * Writes a {@code long} to this instance. - * - * @param value the value to write - */ - public void writeLong(long value); + /** + * Writes a {@code long} 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 {@code 1..5;} the number of bytes actually written - */ - public int writeUleb128(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, treated as an unsigned value + * @return {@code 1..5;} the number of bytes actually written + */ + public int writeUleb128(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 {@code 1..5;} the number of bytes actually written - */ - public int writeSleb128(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 {@code 1..5;} the number of bytes actually written + */ + public int writeSleb128(int value); - /** - * Writes a {@link ByteArray} to this instance. - * - * @param bytes {@code non-null;} the array to write - */ - public void write(ByteArray bytes); + /** + * Writes a {@link ByteArray} to this instance. + * + * @param bytes {@code non-null;} the array to write + */ + public void write(ByteArray bytes); - /** - * Writes a portion of a {@code byte[]} to this instance. - * - * @param bytes {@code non-null;} the array to write - * @param offset {@code >= 0;} offset into {@code bytes} for the first - * byte to write - * @param length {@code >= 0;} number of bytes to write - */ - public void write(byte[] bytes, int offset, int length); + /** + * Writes a portion of a {@code byte[]} to this instance. + * + * @param bytes {@code non-null;} the array to write + * @param offset {@code >= 0;} offset into {@code bytes} for the first + * byte to write + * @param length {@code >= 0;} number of bytes to write + */ + public void write(byte[] bytes, int offset, int length); - /** - * Writes a {@code byte[]} to this instance. This is just - * a convenient shorthand for {@code write(bytes, 0, bytes.length)}. - * - * @param bytes {@code non-null;} the array to write - */ - public void write(byte[] bytes); + /** + * Writes a {@code byte[]} to this instance. This is just + * a convenient shorthand for {@code write(bytes, 0, bytes.length)}. + * + * @param bytes {@code non-null;} the array to write + */ + public void write(byte[] bytes); - /** - * Writes the given number of {@code 0} bytes. - * - * @param count {@code >= 0;} the number of zeroes to write - */ - public void writeZeroes(int count); + /** + * Writes the given number of {@code 0} bytes. + * + * @param count {@code >= 0;} the number of zeroes to write + */ + public void writeZeroes(int count); - /** - * Adds extra bytes if necessary (with value {@code 0}) to - * force alignment of the output cursor as given. - * - * @param alignment {@code > 0;} the alignment; must be a power of two - */ - public void alignTo(int alignment); + /** + * Adds extra bytes if necessary (with value {@code 0}) to + * force alignment of the output cursor as given. + * + * @param alignment {@code > 0;} the alignment; must be a power of two + */ + public void alignTo(int alignment); } diff --git a/dx/src/com/android/jack/dx/util/ToHuman.java b/dx/src/com/android/jack/dx/util/ToHuman.java index a74e670..6158789 100644 --- a/dx/src/com/android/jack/dx/util/ToHuman.java +++ b/dx/src/com/android/jack/dx/util/ToHuman.java @@ -21,11 +21,11 @@ package com.android.jack.dx.util; * a complete but often hard to read) string form. */ public interface ToHuman { - /** - * Return the "human" string form of this instance. This is - * generally less "debuggy" than {@code toString()}. - * - * @return {@code non-null;} the human string form - */ - public String toHuman(); + /** + * Return the "human" string form of this instance. This is + * generally less "debuggy" than {@code toString()}. + * + * @return {@code non-null;} the human string form + */ + public String toHuman(); } diff --git a/dx/src/com/android/jack/dx/util/TwoColumnOutput.java b/dx/src/com/android/jack/dx/util/TwoColumnOutput.java index afce5bb..204c20f 100644 --- a/dx/src/com/android/jack/dx/util/TwoColumnOutput.java +++ b/dx/src/com/android/jack/dx/util/TwoColumnOutput.java @@ -28,227 +28,220 @@ import java.io.Writer; * one which goes on the right. */ public final class TwoColumnOutput { - /** {@code non-null;} underlying writer for final output */ - private final Writer out; - - /** {@code > 0;} the left column width */ - private final int leftWidth; - - /** {@code non-null;} pending left column output */ - private final StringBuffer leftBuf; - - /** {@code non-null;} pending right column output */ - private final StringBuffer rightBuf; - - /** {@code non-null;} left column writer */ - private final IndentingWriter leftColumn; - - /** {@code 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 {@code non-null;} first string - * @param width1 {@code > 0;} width of the first column - * @param spacer {@code non-null;} spacer string - * @param s2 {@code non-null;} second string - * @param width2 {@code > 0;} width of the second column - * @return {@code 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(); + /** {@code non-null;} underlying writer for final output */ + private final Writer out; + + /** {@code > 0;} the left column width */ + private final int leftWidth; + + /** {@code non-null;} pending left column output */ + private final StringBuffer leftBuf; + + /** {@code non-null;} pending right column output */ + private final StringBuffer rightBuf; + + /** {@code non-null;} left column writer */ + private final IndentingWriter leftColumn; + + /** {@code 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 {@code non-null;} first string + * @param width1 {@code > 0;} width of the first column + * @param spacer {@code non-null;} spacer string + * @param s2 {@code non-null;} second string + * @param width2 {@code > 0;} width of the second column + * @return {@code 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); } - /** - * Constructs an instance. - * - * @param out {@code non-null;} writer to send final output to - * @param leftWidth {@code > 0;} width of the left column, in characters - * @param rightWidth {@code > 0;} width of the right column, in characters - * @param spacer {@code 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); + twoOut.flush(); + return sw.toString(); + } + + /** + * Constructs an instance. + * + * @param out {@code non-null;} writer to send final output to + * @param leftWidth {@code > 0;} width of the left column, in characters + * @param rightWidth {@code > 0;} width of the right column, in characters + * @param spacer {@code 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"); } - /** - * Constructs an instance. - * - * @param out {@code non-null;} stream to send final output to - * @param leftWidth {@code >= 1;} width of the left column, in characters - * @param rightWidth {@code >= 1;} width of the right column, in characters - * @param spacer {@code 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); + if (leftWidth < 1) { + throw new IllegalArgumentException("leftWidth < 1"); } - /** - * Gets the writer to use to write to the left column. - * - * @return {@code non-null;} the left column writer - */ - public Writer getLeft() { - return leftColumn; + if (rightWidth < 1) { + throw new IllegalArgumentException("rightWidth < 1"); } - /** - * Gets the writer to use to write to the right column. - * - * @return {@code non-null;} the right column writer - */ - public Writer getRight() { - return rightColumn; + if (spacer == null) { + throw new NullPointerException("spacer == null"); } - /** - * 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); - } + 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 {@code non-null;} stream to send final output to + * @param leftWidth {@code >= 1;} width of the left column, in characters + * @param rightWidth {@code >= 1;} width of the right column, in characters + * @param spacer {@code 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 {@code non-null;} the left column writer + */ + public Writer getLeft() { + return leftColumn; + } + + /** + * Gets the writer to use to write to the right column. + * + * @return {@code 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); - } + } + + /** + * 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 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(); - } + } + + /** + * 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 {@code non-null;} the buffer in question - * @param out {@code 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'); - } + } + + /** + * 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 {@code non-null;} the buffer in question + * @param out {@code 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 {@code non-null;} where to write - * @param amt {@code >= 0;} the number of spaces to write - */ - private static void writeSpaces(Writer out, int amt) throws IOException { - while (amt > 0) { - out.write(' '); - amt--; - } + } + + /** + * Writes the given number of spaces to the given writer. + * + * @param out {@code non-null;} where to write + * @param amt {@code >= 0;} the number of spaces to write + */ + private static void writeSpaces(Writer out, int amt) throws IOException { + while (amt > 0) { + out.write(' '); + amt--; } + } } diff --git a/dx/src/com/android/jack/dx/util/Uint.java b/dx/src/com/android/jack/dx/util/Uint.java index f84a988..3bccc92 100644 --- a/dx/src/com/android/jack/dx/util/Uint.java +++ b/dx/src/com/android/jack/dx/util/Uint.java @@ -20,13 +20,14 @@ package com.android.jack.dx.util; * An unsigned integer. */ public final class Uint implements Comparable<Uint> { - public final int intValue; + public final int intValue; - public Uint(int value) { - this.intValue = value; - } + public Uint(int value) { + this.intValue = value; + } - public int compareTo(Uint uint) { - return Unsigned.compare(intValue, uint.intValue); - } + @Override + public int compareTo(Uint uint) { + return Unsigned.compare(intValue, uint.intValue); + } } diff --git a/dx/src/com/android/jack/dx/util/Unsigned.java b/dx/src/com/android/jack/dx/util/Unsigned.java index 63c8e1d..cd20cde 100644 --- a/dx/src/com/android/jack/dx/util/Unsigned.java +++ b/dx/src/com/android/jack/dx/util/Unsigned.java @@ -20,23 +20,23 @@ package com.android.jack.dx.util; * Unsigned arithmetic over Java's signed types. */ public final class Unsigned { - private Unsigned() {} + private Unsigned() {} - public static int compare(short ushortA, short ushortB) { - if (ushortA == ushortB) { - return 0; - } - int a = ushortA & 0xFFFF; - int b = ushortB & 0xFFFF; - return a < b ? -1 : 1; + public static int compare(short ushortA, short ushortB) { + if (ushortA == ushortB) { + return 0; } + int a = ushortA & 0xFFFF; + int b = ushortB & 0xFFFF; + return a < b ? -1 : 1; + } - public static int compare(int uintA, int uintB) { - if (uintA == uintB) { - return 0; - } - long a = uintA & 0xFFFFFFFFL; - long b = uintB & 0xFFFFFFFFL; - return a < b ? -1 : 1; + public static int compare(int uintA, int uintB) { + if (uintA == uintB) { + return 0; } + long a = uintA & 0xFFFFFFFFL; + long b = uintB & 0xFFFFFFFFL; + return a < b ? -1 : 1; + } } diff --git a/dx/src/com/android/jack/dx/util/Warning.java b/dx/src/com/android/jack/dx/util/Warning.java index effb49f..b941091 100644 --- a/dx/src/com/android/jack/dx/util/Warning.java +++ b/dx/src/com/android/jack/dx/util/Warning.java @@ -20,12 +20,15 @@ package com.android.jack.dx.util; * Exception which is meant to indicate a non-fatal warning. */ public class Warning extends RuntimeException { - /** - * Constructs an instance. - * - * @param message human-oriented message - */ - public Warning(String message) { - super(message); - } + + private static final long serialVersionUID = 1L; + + /** + * Constructs an instance. + * + * @param message human-oriented message + */ + public Warning(String message) { + super(message); + } } diff --git a/dx/src/com/android/jack/dx/util/Writers.java b/dx/src/com/android/jack/dx/util/Writers.java index 3d5c8d1..e13d7c7 100644 --- a/dx/src/com/android/jack/dx/util/Writers.java +++ b/dx/src/com/android/jack/dx/util/Writers.java @@ -23,26 +23,26 @@ import java.io.Writer; * Utilities for dealing with {@code Writer}s. */ public final class Writers { - /** - * This class is uninstantiable. - */ - private Writers() { - // This space intentionally left blank. - } - - /** - * Makes a {@code PrintWriter} for the given {@code Writer}, - * returning the given writer if it already happens to be the right - * class. - * - * @param writer {@code non-null;} writer to (possibly) wrap - * @return {@code non-null;} an appropriate instance - */ - public static PrintWriter printWriterFor(Writer writer) { - if (writer instanceof PrintWriter) { - return (PrintWriter) writer; - } + /** + * This class is uninstantiable. + */ + private Writers() { + // This space intentionally left blank. + } - return new PrintWriter(writer); + /** + * Makes a {@code PrintWriter} for the given {@code Writer}, + * returning the given writer if it already happens to be the right + * class. + * + * @param writer {@code non-null;} writer to (possibly) wrap + * @return {@code non-null;} an appropriate instance + */ + public static PrintWriter printWriterFor(Writer writer) { + if (writer instanceof PrintWriter) { + return (PrintWriter) writer; } + + return new PrintWriter(writer); + } } -- cgit v1.1